summaryrefslogtreecommitdiff
path: root/node_modules/mongoose
diff options
context:
space:
mode:
authorJules Laplace <jules@okfoc.us>2012-09-24 16:22:07 -0400
committerJules Laplace <jules@okfoc.us>2012-09-24 16:22:07 -0400
commit686106d544ecc3b6ffd4db2b665d3bc879a58d8c (patch)
treea5b5e50237cef70e12f0745371896e96f5f6d578 /node_modules/mongoose
ok
Diffstat (limited to 'node_modules/mongoose')
-rw-r--r--node_modules/mongoose/.npmignore5
-rw-r--r--node_modules/mongoose/.travis.yml4
-rw-r--r--node_modules/mongoose/History.md831
-rw-r--r--node_modules/mongoose/Makefile25
-rw-r--r--node_modules/mongoose/README.md356
-rw-r--r--node_modules/mongoose/benchmarks/clone.js60
-rw-r--r--node_modules/mongoose/benchmarks/index.js128
-rw-r--r--node_modules/mongoose/benchmarks/mem.js149
-rw-r--r--node_modules/mongoose/docs/defaults.md29
-rw-r--r--node_modules/mongoose/docs/embedded-documents.md79
-rw-r--r--node_modules/mongoose/docs/errors.md54
-rw-r--r--node_modules/mongoose/docs/finding-documents.md124
-rw-r--r--node_modules/mongoose/docs/getters-setters.md52
-rw-r--r--node_modules/mongoose/docs/indexes.md43
-rw-r--r--node_modules/mongoose/docs/methods-statics.md55
-rw-r--r--node_modules/mongoose/docs/middleware.md61
-rw-r--r--node_modules/mongoose/docs/migration-guide.md148
-rw-r--r--node_modules/mongoose/docs/model-definition.md142
-rw-r--r--node_modules/mongoose/docs/plugins.md38
-rw-r--r--node_modules/mongoose/docs/populate.md166
-rw-r--r--node_modules/mongoose/docs/query.md362
-rw-r--r--node_modules/mongoose/docs/querystream.md6
-rw-r--r--node_modules/mongoose/docs/schematypes.md197
-rw-r--r--node_modules/mongoose/docs/validation.md92
-rw-r--r--node_modules/mongoose/docs/virtuals.md84
-rw-r--r--node_modules/mongoose/examples/schema.js102
-rw-r--r--node_modules/mongoose/index.js7
-rw-r--r--node_modules/mongoose/lib/collection.js167
-rw-r--r--node_modules/mongoose/lib/connection.js517
-rw-r--r--node_modules/mongoose/lib/document.js1223
-rw-r--r--node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js8
-rw-r--r--node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js176
-rw-r--r--node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js106
-rw-r--r--node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js43
-rw-r--r--node_modules/mongoose/lib/error.js25
-rw-r--r--node_modules/mongoose/lib/index.js368
-rw-r--r--node_modules/mongoose/lib/model.js917
-rw-r--r--node_modules/mongoose/lib/namedscope.js70
-rw-r--r--node_modules/mongoose/lib/promise.js145
-rw-r--r--node_modules/mongoose/lib/query.js1527
-rw-r--r--node_modules/mongoose/lib/querystream.js179
-rw-r--r--node_modules/mongoose/lib/schema.js565
-rw-r--r--node_modules/mongoose/lib/schema/array.js232
-rw-r--r--node_modules/mongoose/lib/schema/boolean.js59
-rw-r--r--node_modules/mongoose/lib/schema/buffer.js106
-rw-r--r--node_modules/mongoose/lib/schema/date.js116
-rw-r--r--node_modules/mongoose/lib/schema/documentarray.js141
-rw-r--r--node_modules/mongoose/lib/schema/index.js22
-rw-r--r--node_modules/mongoose/lib/schema/mixed.js63
-rw-r--r--node_modules/mongoose/lib/schema/number.js142
-rw-r--r--node_modules/mongoose/lib/schema/objectid.js126
-rw-r--r--node_modules/mongoose/lib/schema/string.js180
-rw-r--r--node_modules/mongoose/lib/schemadefault.js32
-rw-r--r--node_modules/mongoose/lib/schematype.js428
-rw-r--r--node_modules/mongoose/lib/statemachine.js179
-rw-r--r--node_modules/mongoose/lib/types/array.js422
-rw-r--r--node_modules/mongoose/lib/types/buffer.js171
-rw-r--r--node_modules/mongoose/lib/types/documentarray.js133
-rw-r--r--node_modules/mongoose/lib/types/embedded.js103
-rw-r--r--node_modules/mongoose/lib/types/index.js14
-rw-r--r--node_modules/mongoose/lib/types/number.js84
-rw-r--r--node_modules/mongoose/lib/types/objectid.js12
-rw-r--r--node_modules/mongoose/lib/utils.js434
-rw-r--r--node_modules/mongoose/lib/virtualtype.js74
-rw-r--r--node_modules/mongoose/package.json52
-rw-r--r--node_modules/mongoose/support/expresso/.gitmodules3
-rw-r--r--node_modules/mongoose/support/expresso/.npmignore3
-rw-r--r--node_modules/mongoose/support/expresso/History.md128
-rw-r--r--node_modules/mongoose/support/expresso/Makefile53
-rw-r--r--node_modules/mongoose/support/expresso/Readme.md61
-rwxr-xr-xnode_modules/mongoose/support/expresso/bin/expresso874
-rw-r--r--node_modules/mongoose/support/expresso/docs/api.html1080
-rw-r--r--node_modules/mongoose/support/expresso/docs/index.html377
-rw-r--r--node_modules/mongoose/support/expresso/docs/index.md290
-rw-r--r--node_modules/mongoose/support/expresso/docs/layout/foot.html3
-rw-r--r--node_modules/mongoose/support/expresso/docs/layout/head.html42
-rw-r--r--node_modules/mongoose/support/expresso/lib/bar.js4
-rw-r--r--node_modules/mongoose/support/expresso/lib/foo.js16
-rw-r--r--node_modules/mongoose/support/expresso/package.json12
-rw-r--r--node_modules/mongoose/support/expresso/test/assert.test.js91
-rw-r--r--node_modules/mongoose/support/expresso/test/async.test.js12
-rw-r--r--node_modules/mongoose/support/expresso/test/bar.test.js13
-rw-r--r--node_modules/mongoose/support/expresso/test/foo.test.js14
-rw-r--r--node_modules/mongoose/support/expresso/test/http.test.js109
-rw-r--r--node_modules/mongoose/support/expresso/test/serial/async.test.js39
-rw-r--r--node_modules/mongoose/support/expresso/test/serial/http.test.js48
-rw-r--r--node_modules/mongoose/test/collection.test.js116
-rw-r--r--node_modules/mongoose/test/common.js145
-rw-r--r--node_modules/mongoose/test/connection.test.js310
-rw-r--r--node_modules/mongoose/test/crash.test.js36
-rw-r--r--node_modules/mongoose/test/document.strict.test.js178
-rw-r--r--node_modules/mongoose/test/document.test.js912
-rw-r--r--node_modules/mongoose/test/drivers/node-mongodb-native/collection.test.js63
-rwxr-xr-xnode_modules/mongoose/test/dropdb.js7
-rw-r--r--node_modules/mongoose/test/index.test.js235
-rw-r--r--node_modules/mongoose/test/model.querying.test.js2352
-rw-r--r--node_modules/mongoose/test/model.ref.test.js1528
-rw-r--r--node_modules/mongoose/test/model.stream.test.js232
-rw-r--r--node_modules/mongoose/test/model.test.js4682
-rw-r--r--node_modules/mongoose/test/model.update.test.js526
-rw-r--r--node_modules/mongoose/test/namedscope.test.js253
-rw-r--r--node_modules/mongoose/test/promise.test.js167
-rw-r--r--node_modules/mongoose/test/query.test.js890
-rw-r--r--node_modules/mongoose/test/schema.onthefly.test.js105
-rw-r--r--node_modules/mongoose/test/schema.test.js978
-rw-r--r--node_modules/mongoose/test/shard.test.js216
-rw-r--r--node_modules/mongoose/test/types.array.test.js551
-rw-r--r--node_modules/mongoose/test/types.buffer.test.js350
-rw-r--r--node_modules/mongoose/test/types.document.test.js197
-rw-r--r--node_modules/mongoose/test/types.documentarray.test.js132
-rw-r--r--node_modules/mongoose/test/types.number.test.js37
-rw-r--r--node_modules/mongoose/test/utils.test.js196
-rw-r--r--node_modules/mongoose/test/zzlast.test.js11
113 files changed, 31437 insertions, 0 deletions
diff --git a/node_modules/mongoose/.npmignore b/node_modules/mongoose/.npmignore
new file mode 100644
index 0000000..a3db131
--- /dev/null
+++ b/node_modules/mongoose/.npmignore
@@ -0,0 +1,5 @@
+lib-cov
+**.swp
+*.sw*
+node_modules/
+*.orig
diff --git a/node_modules/mongoose/.travis.yml b/node_modules/mongoose/.travis.yml
new file mode 100644
index 0000000..f1d0f13
--- /dev/null
+++ b/node_modules/mongoose/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+ - 0.4
+ - 0.6
diff --git a/node_modules/mongoose/History.md b/node_modules/mongoose/History.md
new file mode 100644
index 0000000..a06b0b4
--- /dev/null
+++ b/node_modules/mongoose/History.md
@@ -0,0 +1,831 @@
+2.5.13 / 2012-03-22
+===================
+
+ * fixed; failing validation of unselected required paths (#730,#713)
+ * fixed; emitting connection error when only one listener (#759)
+ * fixed; MongooseArray#splice was not returning values (#784) [chrisleishman]
+
+2.5.12 / 2012-03-21
+===================
+
+ * fixed; honor the `safe` option in all ensureIndex calls
+ * updated; node-mongodb-native driver to 0.9.9-7
+
+2.5.11 / 2012-03-15
+===================
+
+ * added; introspection for getters/setters (#745)
+ * updated; node-mongodb-driver to 0.9.9-5
+ * added; tailable method to Query (#769) [holic]
+ * fixed; Number min/max validation of null (#764) [btamas]
+ * added; more flexible user/password connection options (#738) [KarneAsada]
+
+2.5.10 / 2012-03-06
+===================
+
+ * updated; node-mongodb-native driver to 0.9.9-4
+ * added; Query#comment()
+ * fixed; allow unsetting arrays
+ * fixed; hooking the set method of subdocuments (#746)
+ * fixed; edge case in hooks
+ * fixed; allow $id and $ref in queries (fixes compatibility with mongoose-dbref) (#749) [richtera]
+ * added; default path selection to SchemaTypes
+
+2.5.9 / 2012-02-22
+===================
+
+ * fixed; properly cast nested atomic update operators for sub-documents
+
+2.5.8 / 2012-02-21
+===================
+
+ * added; post 'remove' middleware includes model that was removed (#729) [timoxley]
+
+2.5.7 / 2012-02-09
+===================
+
+ * fixed; RegExp validators on node >= v0.6.x
+
+2.5.6 / 2012-02-09
+===================
+
+ * fixed; emit errors returned from db.collection() on the connection (were being swallowed)
+ * added; can add multiple validators in your schema at once (#718) [diogogmt]
+ * fixed; strict embedded documents (#717)
+ * updated; docs [niemyjski]
+ * added; pass number of affected docs back in model.update/save
+
+2.5.5 / 2012-02-03
+===================
+
+ * fixed; RangeError: maximum call stack exceed error when removing docs with Number _id (#714)
+
+2.5.4 / 2012-02-03
+===================
+
+ * fixed; RangeError: maximum call stack exceed error (#714)
+
+2.5.3 / 2012-02-02
+===================
+
+ * added; doc#isSelected(path)
+ * added; query#equals()
+ * added; beta sharding support
+ * added; more descript error msgs (#700) [obeleh]
+ * added; document.modifiedPaths (#709) [ljharb]
+ * fixed; only functions can be added as getters/setters (#707,704) [ljharb]
+
+2.5.2 / 2012-01-30
+===================
+
+ * fixed; rollback -native driver to 0.9.7-3-5 (was causing timeouts and other replica set weirdness)
+ * deprecated; MongooseNumber (will be moved to a separate repo for 3.x)
+ * added; init event is emitted on schemas
+
+2.5.1 / 2012-01-27
+===================
+
+ * fixed; honor strict schemas in Model.update (#699)
+
+2.5.0 / 2012-01-26
+===================
+
+ * added; doc.toJSON calls toJSON on embedded docs when exists [jerem]
+ * added; populate support for refs of type Buffer (#686) [jerem]
+ * added; $all support for ObjectIds and Dates (#690)
+ * fixed; virtual setter calling on instantiation when strict: true (#682) [hunterloftis]
+ * fixed; doc construction triggering getters (#685)
+ * fixed; MongooseBuffer check in deepEquals (#688)
+ * fixed; range error when using Number _ids with `instance.save()` (#691)
+ * fixed; isNew on embedded docs edge case (#680)
+ * updated; driver to 0.9.8-3
+ * updated; expose `model()` method within static methods
+
+2.4.10 / 2012-01-10
+===================
+
+ * added; optional getter application in .toObject()/.toJSON() (#412)
+ * fixed; nested $operators in $all queries (#670)
+ * added; $nor support (#674)
+ * fixed; bug when adding nested schema (#662) [paulwe]
+
+2.4.9 / 2012-01-04
+===================
+
+ * updated; driver to 0.9.7-3-5 to fix Linux performance degradation on some boxes
+
+2.4.8 / 2011-12-22
+===================
+
+ * updated; bump -native to 0.9.7.2-5
+ * fixed; compatibility with date.js (#646) [chrisleishman]
+ * changed; undocumented schema "lax" option to "strict"
+ * fixed; default value population for strict schemas
+ * updated; the nextTick helper for small performance gain. 1bee2a2
+
+2.4.7 / 2011-12-16
+===================
+
+ * fixed; bug in 2.4.6 with path setting
+ * updated; bump -native to 0.9.7.2-1
+ * added; strict schema option [nw]
+
+2.4.6 / 2011-12-16
+===================
+
+ * fixed; conflicting mods on update bug [sirlantis]
+ * improved; doc.id getter performance
+
+2.4.5 / 2011-12-14
+===================
+
+ * fixed; bad MongooseArray behavior in 2.4.2 - 2.4.4
+
+2.4.4 / 2011-12-14
+===================
+
+ * fixed; MongooseArray#doAtomics throwing after sliced
+
+2.4.3 / 2011-12-14
+===================
+
+ * updated; system.profile schema for MongoDB 2x
+
+2.4.2 / 2011-12-12
+===================
+
+ * fixed; partially populating multiple children of subdocs (#639) [kenpratt]
+ * fixed; allow Update of numbers to null (#640) [jerem]
+
+2.4.1 / 2011-12-02
+===================
+
+ * added; options support for populate() queries
+ * updated; -native driver to 0.9.7-1.4
+
+2.4.0 / 2011-11-29
+===================
+
+ * added; QueryStreams (#614)
+ * added; debug print mode for development
+ * added; $within support to Array queries (#586) [ggoodale]
+ * added; $centerSphere query support
+ * fixed; $within support
+ * added; $unset is now used when setting a path to undefined (#519)
+ * added; query#batchSize support
+ * updated; docs
+ * updated; -native driver to 0.9.7-1.3 (provides Windows support)
+
+2.3.13 / 2011-11-15
+===================
+
+ * fixed; required validation for Refs (#612) [ded]
+ * added; $nearSphere support for Arrays (#610)
+
+2.3.12 / 2011-11-09
+===================
+
+ * fixed; regression, objects passed to Model.update should not be changed (#605)
+ * fixed; regression, empty Model.update should not be executed
+
+2.3.11 / 2011-11-08
+===================
+
+ * fixed; using $elemMatch on arrays of Mixed types (#591)
+ * fixed; allow using $regex when querying Arrays (#599)
+ * fixed; calling Model.update with no atomic keys (#602)
+
+2.3.10 / 2011-11-05
+===================
+
+ * fixed; model.update casting for nested paths works (#542)
+
+2.3.9 / 2011-11-04
+==================
+
+ * fixed; deepEquals check for MongooseArray returned false
+ * fixed; reset modified flags of embedded docs after save [gitfy]
+ * fixed; setting embedded doc with identical values no longer marks modified [gitfy]
+ * updated; -native driver to 0.9.6.23 [mlazarov]
+ * fixed; Model.update casting (#542, #545, #479)
+ * fixed; populated refs no longer fail required validators (#577)
+ * fixed; populating refs of objects with custom ids works
+ * fixed; $pop & $unset work with Model.update (#574)
+ * added; more helpful debugging message for Schema#add (#578)
+ * fixed; accessing .id when no _id exists now returns null (#590)
+
+2.3.8 / 2011-10-26
+==================
+
+ * added; callback to query#findOne is now optional (#581)
+
+2.3.7 / 2011-10-24
+==================
+
+ * fixed; wrapped save/remove callbacks in nextTick to mitigate -native swallowing thrown errors
+
+2.3.6 / 2011-10-21
+==================
+
+ * fixed; exclusion of embedded doc _id from query results (#541)
+
+2.3.5 / 2011-10-19
+==================
+
+ * fixed; calling queries without passing a callback works (#569)
+ * fixed; populate() works with String and Number _ids too (#568)
+
+2.3.4 / 2011-10-18
+==================
+
+ * added; Model.create now accepts an array as a first arg
+ * fixed; calling toObject on a DocumentArray with nulls no longer throws
+ * fixed; calling inspect on a DocumentArray with nulls no longer throws
+ * added; MongooseArray#unshift support
+ * fixed; save hooks now fire on embedded documents [gitfy] (#456)
+ * updated; -native driver to 0.9.6-22
+ * fixed; correctly pass $addToSet op instead of $push
+ * fixed; $addToSet properly detects dates
+ * fixed; $addToSet with multiple items works
+ * updated; better node 0.6 Buffer support
+
+2.3.3 / 2011-10-12
+==================
+
+ * fixed; population conditions in multi-query settings [vedmalex] (#563)
+ * fixed; now compatible with Node v0.5.x
+
+2.3.2 / 2011-10-11
+==================
+
+ * fixed; population of null subdoc properties no longer hangs (#561)
+
+2.3.1 / 2011-10-10
+==================
+
+ * added; support for Query filters to populate() [eneko]
+ * fixed; querying with number no longer crashes mongodb (#555) [jlbyrey]
+ * updated; version of -native driver to 0.9.6-21
+ * fixed; prevent query callbacks that throw errors from corrupting -native connection state
+
+2.3.0 / 2011-10-04
+==================
+
+ * fixed; nulls as default values for Boolean now works as expected
+ * updated; version of -native driver to 0.9.6-20
+
+2.2.4 / 2011-10-03
+==================
+
+ * fixed; populate() works when returned array contains undefined/nulls
+
+2.2.3 / 2011-09-29
+==================
+
+ * updated; version of -native driver to 0.9.6-19
+
+2.2.2 / 2011-09-28
+==================
+
+ * added; $regex support to String [davidandrewcope]
+ * added; support for other contexts like repl etc (#535)
+ * fixed; clear modified state properly after saving
+ * added; $addToSet support to Array
+
+2.2.1 / 2011-09-22
+==================
+
+ * more descript error when casting undefined to string
+ * updated; version of -native driver to 0.9.6-18
+
+2.2.0 / 2011-09-22
+==================
+
+ * fixed; maxListeners warning on schemas with many arrays (#530)
+ * changed; return / apply defaults based on fields selected in query (#423)
+ * fixed; correctly detect Mixed types within schema arrays (#532)
+
+2.1.4 / 2011-09-20
+==================
+
+ * fixed; new private methods that stomped on users code
+ * changed; finished removing old "compat" support which did nothing
+
+2.1.3 / 2011-09-16
+==================
+
+ * updated; version of -native driver to 0.9.6-15
+ * added; emit `error` on connection when open fails [edwardhotchkiss]
+ * added; index support to Buffers (thanks justmoon for helping track this down)
+ * fixed; passing collection name via schema in conn.model() now works (thanks vedmalex for reporting)
+
+2.1.2 / 2011-09-07
+==================
+
+ * fixed; Query#find with no args no longer throws
+
+2.1.1 / 2011-09-07
+==================
+
+ * added; support Model.count(fn)
+ * fixed; compatibility with node >=0.4.0 < 0.4.3
+ * added; pass model.options.safe through with .save() so w:2, wtimeout:5000 options work [andrewjstone]
+ * added; support for $type queries
+ * added; support for Query#or
+ * added; more tests
+ * optimized populate queries
+
+2.1.0 / 2011-09-01
+==================
+
+ * changed; document#validate is a public method
+ * fixed; setting number to same value no longer marks modified (#476) [gitfy]
+ * fixed; Buffers shouldn't have default vals
+ * added; allow specifying collection name in schema (#470) [ixti]
+ * fixed; reset modified paths and atomics after saved (#459)
+ * fixed; set isNew on embedded docs to false after save
+ * fixed; use self to ensure proper scope of options in doOpenSet (#483) [andrewjstone]
+
+2.0.4 / 2011-08-29
+==================
+
+ * Fixed; Only send the depopulated ObjectId instead of the entire doc on save (DBRefs)
+ * Fixed; Properly cast nested array values in Model.update (the data was stored in Mongo incorrectly but recast on document fetch was "fixing" it)
+
+2.0.3 / 2011-08-28
+==================
+
+ * Fixed; manipulating a populated array no longer causes infinite loop in BSON serializer during save (#477)
+ * Fixed; populating an empty array no longer hangs foreeeeeeeever (#481)
+
+2.0.2 / 2011-08-25
+==================
+
+ * Fixed; Maintain query option key order (fixes 'bad hint' error from compound query hints)
+
+2.0.1 / 2011-08-25
+==================
+
+ * Fixed; do not over-write the doc when no valide props exist in Model.update (#473)
+
+2.0.0 / 2011-08-24
+===================
+
+ * Added; support for Buffers [justmoon]
+ * Changed; improved error handling [maelstrom]
+ * Removed: unused utils.erase
+ * Fixed; support for passing other context object into Schemas (#234) [Sija]
+ * Fixed; getters are no longer circular refs to themselves (#366)
+ * Removed; unused compat.js
+ * Fixed; getter/setter scopes are set properly
+ * Changed; made several private properties more obvious by prefixing _
+ * Added; DBRef support [guille]
+ * Changed; removed support for multiple collection names per model
+ * Fixed; no longer applying setters when document returned from db
+ * Changed; default auto_reconnect to true
+ * Changed; Query#bind no longer clones the query
+ * Fixed; Model.update now accepts $pull, $inc and friends (#404)
+ * Added; virtual type option support [nw]
+
+1.8.4 / 2011-08-21
+===================
+
+ * Fixed; validation bug when instantiated with non-schema properties (#464) [jmreidy]
+
+1.8.3 / 2011-08-19
+===================
+
+ * Fixed; regression in connection#open [jshaw86]
+
+1.8.2 / 2011-08-17
+===================
+
+ * fixed; reset connection.readyState after failure [tomseago]
+ * fixed; can now query positionally for non-embedded docs (arrays of numbers/strings etc)
+ * fixed; embedded document query casting
+ * added; support for passing options to node-mongo-native db, server, and replsetserver [tomseago]
+
+1.8.1 / 2011-08-10
+===================
+
+ * fixed; ObjectIds were always marked modified
+ * fixed; can now query using document instances
+ * fixed; can now query/update using documents with subdocs
+
+1.8.0 / 2011-08-04
+===================
+
+ * fixed; can now use $all with String and Number
+ * fixed; can query subdoc array with $ne: null
+ * fixed; instance.subdocs#id now works with custom _ids
+ * fixed; do not apply setters when doc returned from db (change in bad behavior)
+
+1.7.4 / 2011-07-25
+===================
+
+ * fixed; sparse now a valid seperate schema option
+ * fixed; now catching cast errors in queries
+ * fixed; calling new Schema with object created in vm.runInNewContext now works (#384) [Sija]
+ * fixed; String enum was disallowing null
+ * fixed; Find by nested document _id now works (#389)
+
+1.7.3 / 2011-07-16
+===================
+
+ * fixed; MongooseArray#indexOf now works with ObjectIds
+ * fixed; validation scope now set properly (#418)
+ * fixed; added missing colors dependency (#398)
+
+1.7.2 / 2011-07-13
+===================
+
+ * changed; node-mongodb-native driver to v0.9.6.7
+
+1.7.1 / 2011-07-12
+===================
+
+ * changed; roll back node-mongodb-native driver to v0.9.6.4
+
+1.7.0 / 2011-07-12
+===================
+
+ * fixed; collection name misspelling [mathrawka]
+ * fixed; 2nd param is required for ReplSetServers [kevinmarvin]
+ * fixed; MongooseArray behaves properly with Object.keys
+ * changed; node-mongodb-native driver to v0.9.6.6
+ * fixed/changed; Mongodb segfault when passed invalid ObjectId (#407)
+ - This means invalid data passed to the ObjectId constructor will now error
+
+1.6.0 / 2011-07-07
+===================
+
+ * changed; .save() errors are now emitted on the instances db instead of the instance 9782463fc
+ * fixed; errors occurring when creating indexes now properly emit on db
+ * added; $maxDistance support to MongooseArrays
+ * fixed; RegExps now work with $all
+ * changed; node-mongodb-native driver to v0.9.6.4
+ * fixed; model names are now accessible via .modelName
+ * added; Query#slaveOk support
+
+1.5.0 / 2011-06-27
+===================
+
+ * changed; saving without a callback no longer ignores the error (@bnoguchi)
+ * changed; hook-js version bump to 0.1.9
+ * changed; node-mongodb-native version bumped to 0.9.6.1 - When .remove() doesn't
+ return an error, null is no longer passed.
+ * fixed; two memory leaks (@justmoon)
+ * added; sparse index support
+ * added; more ObjectId conditionals (gt, lt, gte, lte) (@phillyqueso)
+ * added; options are now passed in model#remote (@JerryLuke)
+
+1.4.0 / 2011-06-10
+===================
+
+ * bumped hooks-js dependency (fixes issue passing null as first arg to next())
+ * fixed; document#inspect now works properly with nested docs
+ * fixed; 'set' now works as a schema attribute (GH-365)
+ * fixed; _id is now set properly within pre-init hooks (GH-289)
+ * added; Query#distinct / Model#distinct support (GH-155)
+ * fixed; embedded docs now can use instance methods (GH-249)
+ * fixed; can now overwrite strings conflicting with schema type
+
+1.3.7 / 2011-06-03
+===================
+
+ * added MongooseArray#splice support
+ * fixed; 'path' is now a valid Schema pathname
+ * improved hooks (utilizing https://github.com/bnoguchi/hooks-js)
+ * fixed; MongooseArray#$shift now works (never did)
+ * fixed; Document.modified no longer throws
+ * fixed; modifying subdoc property sets modified paths for subdoc and parent doc
+ * fixed; marking subdoc path as modified properly persists the value to the db
+ * fixed; RexExps can again be saved ( #357 )
+
+1.3.6 / 2011-05-18
+===================
+
+ * fixed; corrected casting for queries against array types
+ * added; Document#set now accepts Document instances
+
+1.3.5 / 2011-05-17
+===================
+
+ * fixed; $ne queries work properly with single vals
+ * added; #inspect() methods to improve console.log output
+
+1.3.4 / 2011-05-17
+===================
+
+ * fixed; find by Date works as expected (#336)
+ * added; geospatial 2d index support
+ * added; support for $near (#309)
+ * updated; node-mongodb-native driver
+ * fixed; updating numbers work (#342)
+ * added; better error msg when try to remove an embedded doc without an _id (#307)
+ * added; support for 'on-the-fly' schemas (#227)
+ * changed; virtual id getters can now be skipped
+ * fixed; .index() called on subdoc schema now works as expected
+ * fixed; db.setProfile() now buffers until the db is open (#340)
+
+1.3.3 / 2011-04-27
+===================
+
+ * fixed; corrected query casting on nested mixed types
+
+1.3.2 / 2011-04-27
+===================
+
+ * fixed; query hints now retain key order
+
+1.3.1 / 2011-04-27
+===================
+
+ * fixed; setting a property on an embedded array no longer overwrites entire array (GH-310)
+ * fixed; setting nested properties works when sibling prop is named "type"
+ * fixed; isModified is now much finer grained when .set() is used (GH-323)
+ * fixed; mongoose.model() and connection.model() now return the Model (GH-308, GH-305)
+ * fixed; can now use $gt, $lt, $gte, $lte with String schema types (GH-317)
+ * fixed; .lowercase() -> .toLowerCase() in pluralize()
+ * fixed; updating an embedded document by index works (GH-334)
+ * changed; .save() now passes the instance to the callback (GH-294, GH-264)
+ * added; can now query system.profile and system.indexes collections
+ * added; db.model('system.profile') is now included as a default Schema
+ * added; db.setProfiling(level, ms, callback)
+ * added; Query#hint() support
+ * added; more tests
+ * updated node-mongodb-native to 0.9.3
+
+1.3.0 / 2011-04-19
+===================
+
+ * changed; save() callbacks now fire only once on failed validation
+ * changed; Errors returned from save() callbacks now instances of ValidationError
+ * fixed; MongooseArray#indexOf now works properly
+
+1.2.0 / 2011-04-11
+===================
+
+ * changed; MongooseNumber now casts empty string to null
+
+1.1.25 / 2011-04-08
+===================
+
+ * fixed; post init now fires at proper time
+
+1.1.24 / 2011-04-03
+===================
+
+ * fixed; pushing an array onto an Array works on existing docs
+
+1.1.23 / 2011-04-01
+===================
+
+ * Added Model#model
+
+1.1.22 / 2011-03-31
+===================
+
+ * Fixed; $in queries on mixed types now work
+
+1.1.21 / 2011-03-31
+===================
+
+ * Fixed; setting object root to null/undefined works
+
+1.1.20 / 2011-03-31
+===================
+
+ * Fixed; setting multiple props on null field works
+
+1.1.19 / 2011-03-31
+===================
+
+ * Fixed; no longer using $set on paths to an unexisting fields
+
+1.1.18 / 2011-03-30
+===================
+
+ * Fixed; non-mixed type object setters work after initd from null
+
+1.1.17 / 2011-03-30
+===================
+
+ * Fixed; nested object property access works when root initd with null value
+
+1.1.16 / 2011-03-28
+===================
+
+ * Fixed; empty arrays are now saved
+
+1.1.15 / 2011-03-28
+===================
+
+ * Fixed; `null` and `undefined` are set atomically.
+
+1.1.14 / 2011-03-28
+===================
+
+ * Changed; more forgiving date casting, accepting '' as null.
+
+1.1.13 / 2011-03-26
+===================
+
+ * Fixed setting values as `undefined`.
+
+1.1.12 / 2011-03-26
+===================
+
+ * Fixed; nested objects now convert to JSON properly
+ * Fixed; setting nested objects directly now works
+ * Update node-mongodb-native
+
+1.1.11 / 2011-03-25
+===================
+
+ * Fixed for use of `type` as a key.
+
+1.1.10 / 2011-03-23
+===================
+
+ * Changed; Make sure to only ensure indexes while connected
+
+1.1.9 / 2011-03-2
+==================
+
+ * Fixed; Mixed can now default to empty arrays
+ * Fixed; keys by the name 'type' are now valid
+ * Fixed; null values retrieved from the database are hydrated as null values.
+ * Fixed repeated atomic operations when saving a same document twice.
+
+1.1.8 / 2011-03-23
+==================
+
+ * Fixed 'id' overriding. [bnoguchi]
+
+1.1.7 / 2011-03-22
+==================
+
+ * Fixed RegExp query casting when querying against an Array of Strings [bnoguchi]
+ * Fixed getters/setters for nested virtualsl. [bnoguchi]
+
+1.1.6 / 2011-03-22
+==================
+
+ * Only doValidate when path exists in Schema [aheckmann]
+ * Allow function defaults for Array types [aheckmann]
+ * Fix validation hang [aheckmann]
+ * Fix setting of isRequired of SchemaType [aheckmann]
+ * Fix SchemaType#required(false) filter [aheckmann]
+ * More backwards compatibility [aheckmann]
+ * More tests [aheckmann]
+
+1.1.5 / 2011-03-14
+==================
+
+ * Added support for `uri, db, fn` and `uri, fn` signatures for replica sets.
+ * Improved/extended replica set tests.
+
+1.1.4 / 2011-03-09
+==================
+
+ * Fixed; running an empty Query doesn't throw. [aheckmann]
+ * Changed; Promise#addBack returns promise. [aheckmann]
+ * Added streaming cursor support. [aheckmann]
+ * Changed; Query#update defaults to use$SetOnSave now. [brian]
+ * Added more docs.
+
+1.1.3 / 2011-03-04
+==================
+
+ * Added Promise#resolve [aheckmann]
+ * Fixed backward compatibility with nulls [aheckmann]
+ * Changed; Query#{run,exec} return promises [aheckmann]
+
+1.1.2 / 2011-03-03
+==================
+
+ * Restored Query#exec and added notion of default operation [brian]
+ * Fixed ValidatorError messages [brian]
+
+1.1.1 / 2011-03-01
+==================
+
+ * Added SchemaType String `lowercase`, `uppercase`, `trim`.
+ * Public exports (`Model`, `Document`) and tests.
+ * Added ObjectId casting support for `Document`s.
+
+1.1.0 / 2011-02-25
+==================
+
+ * Added support for replica sets.
+
+1.0.16 / 2011-02-18
+===================
+
+ * Added $nin as another whitelisted $conditional for SchemaArray [brian]
+ * Changed #with to #where [brian]
+ * Added ability to use $in conditional with Array types [brian]
+
+1.0.15 / 2011-02-18
+===================
+
+ * Added `id` virtual getter for documents to easily access the hexString of
+ the `_id`.
+
+1.0.14 / 2011-02-17
+===================
+
+ * Fix for arrays within subdocuments [brian]
+
+1.0.13 / 2011-02-16
+===================
+
+ * Fixed embedded documents saving.
+
+1.0.12 / 2011-02-14
+===================
+
+ * Minor refactorings [brian]
+
+1.0.11 / 2011-02-14
+===================
+
+ * Query refactor and $ne, $slice, $or, $size, $elemMatch, $nin, $exists support [brian]
+ * Named scopes sugar [brian]
+
+1.0.10 / 2011-02-11
+===================
+
+ * Updated node-mongodb-native driver [thanks John Allen]
+
+1.0.9 / 2011-02-09
+==================
+
+ * Fixed single member arrays as defaults [brian]
+
+1.0.8 / 2011-02-09
+==================
+
+ * Fixed for collection-level buffering of commands [gitfy]
+ * Fixed `Document#toJSON` [dalejefferson]
+ * Fixed `Connection` authentication [robrighter]
+ * Fixed clash of accessors in getters/setters [eirikurn]
+ * Improved `Model#save` promise handling
+
+1.0.7 / 2011-02-05
+==================
+
+ * Fixed memory leak warnings for test suite on 0.3
+ * Fixed querying documents that have an array that contain at least one
+ specified member. [brian]
+ * Fixed default value for Array types (fixes GH-210). [brian]
+ * Fixed example code.
+
+1.0.6 / 2011-02-03
+==================
+
+ * Fixed `post` middleware
+ * Fixed; it's now possible to instantiate a model even when one of the paths maps
+ to an undefined value [brian]
+
+1.0.5 / 2011-02-02
+==================
+
+ * Fixed; combo $push and $pushAll auto-converts into a $pushAll [brian]
+ * Fixed; combo $pull and $pullAll auto-converts to a single $pullAll [brian]
+ * Fixed; $pullAll now removes said members from array before save (so it acts just
+ like pushAll) [brian]
+ * Fixed; multiple $pulls and $pushes become a single $pullAll and $pushAll.
+ Moreover, $pull now modifies the array before save to reflect the immediate
+ change [brian]
+ * Added tests for nested shortcut getters [brian]
+ * Added tests that show that Schemas with nested Arrays don't apply defaults
+ [brian]
+
+1.0.4 / 2011-02-02
+==================
+
+ * Added MongooseNumber#toString
+ * Added MongooseNumber unit tests
+
+1.0.3 / 2011-02-02
+==================
+
+ * Make sure safe mode works with Model#save
+ * Changed Schema options: safe mode is now the default
+ * Updated node-mongodb-native to HEAD
+
+1.0.2 / 2011-02-02
+==================
+
+ * Added a Model.create shortcut for creating documents. [brian]
+ * Fixed; we can now instantiate models with hashes that map to at least one
+ null value. [brian]
+ * Fixed Schema with more than 2 nested levels. [brian]
+
+1.0.1 / 2011-02-02
+==================
+
+ * Improved `MongooseNumber`, works almost like the native except for `typeof`
+ not being `'number'`.
diff --git a/node_modules/mongoose/Makefile b/node_modules/mongoose/Makefile
new file mode 100644
index 0000000..cdbcc19
--- /dev/null
+++ b/node_modules/mongoose/Makefile
@@ -0,0 +1,25 @@
+
+TESTS = $(shell find test/ -name '*.test.js')
+
+test:
+ @NODE_ENV=test ./support/expresso/bin/expresso \
+ $(TESTFLAGS) \
+ $(TESTS)
+ @node test/dropdb.js
+
+test-cov:
+ @TESTFLAGS=--cov $(MAKE) test
+
+docs: docs/api.html
+
+docs/api.html: lib/mongoose/*.js
+ dox \
+ --private \
+ --title Mongooose \
+ --desc "Expressive MongoDB for Node.JS" \
+ $(shell find lib/mongoose/* -type f) > $@
+
+docclean:
+ rm -f docs/*.{1,html}
+
+.PHONY: test test-cov docs docclean
diff --git a/node_modules/mongoose/README.md b/node_modules/mongoose/README.md
new file mode 100644
index 0000000..214e6f5
--- /dev/null
+++ b/node_modules/mongoose/README.md
@@ -0,0 +1,356 @@
+## What's Mongoose?
+
+Mongoose is a [MongoDB](http://www.mongodb.org/) object modeling tool designed to work in an asynchronous environment.
+
+Defining a model is as easy as:
+
+ var Comments = new Schema({
+ title : String
+ , body : String
+ , date : Date
+ });
+
+ var BlogPost = new Schema({
+ author : ObjectId
+ , title : String
+ , body : String
+ , buf : Buffer
+ , date : Date
+ , comments : [Comments]
+ , meta : {
+ votes : Number
+ , favs : Number
+ }
+ });
+
+ var Post = mongoose.model('BlogPost', BlogPost);
+
+## Installation
+
+The recommended way is through the excellent [NPM](http://www.npmjs.org/):
+
+ $ npm install mongoose
+
+Otherwise, you can check it in your repository and then expose it:
+
+ $ git clone git://github.com/LearnBoost/mongoose.git node_modules/mongoose/
+
+And install dependency modules written on `package.json`.
+
+Then you can `require` it:
+
+ require('mongoose')
+
+## Connecting to MongoDB
+
+First, we need to define a connection. If your app uses only one database, you should use `mongose.connect`. If you need to create additional connections, use `mongoose.createConnection`.
+
+Both `connect` and `createConnection` take a `mongodb://` URI, or the parameters `host, database, port, options`.
+
+ var mongoose = require('mongoose');
+
+ mongoose.connect('mongodb://localhost/my_database');
+
+Once connected, the `open` event is fired on the `Connection` instance. If you're using `mongoose.connect`, the `Connection` is `mongoose.connection`. Otherwise, `mongoose.createConnection` return value is a `Connection`.
+
+**Important!** Mongoose buffers all the commands until it's connected to the database. This means that you don't have to wait until it connects to MongoDB in order to define models, run queries, etc.
+
+## Defining a Model
+
+Models are defined through the `Schema` interface.
+
+ var Schema = mongoose.Schema
+ , ObjectId = Schema.ObjectId;
+
+ var BlogPost = new Schema({
+ author : ObjectId
+ , title : String
+ , body : String
+ , date : Date
+ });
+
+Aside from defining the structure of your documents and the types of data you're storing, a Schema handles the definition of:
+
+* [Validators](http://mongoosejs.com/docs/validation.html) (async and sync)
+* [Defaults](http://mongoosejs.com/docs/schematypes.html)
+* [Getters](http://mongoosejs.com/docs/getters-setters.html)
+* [Setters](http://mongoosejs.com/docs/getters-setters.html)
+* [Indexes](http://mongoosejs.com/docs/indexes.html)
+* [Middleware](http://mongoosejs.com/docs/middleware.html)
+* [Methods](http://mongoosejs.com/docs/methods-statics.html) definition
+* [Statics](http://mongoosejs.com/docs/methods-statics.html) definition
+* [Plugins](http://mongoosejs.com/docs/plugins.html)
+* [DBRefs](http://mongoosejs.com/docs/dbrefs.html)
+
+The following example shows some of these features:
+
+ var Comment = new Schema({
+ name : { type: String, default: 'hahaha' }
+ , age : { type: Number, min: 18, index: true }
+ , bio : { type: String, match: /[a-z]/ }
+ , date : { type: Date, default: Date.now }
+ , buff : Buffer
+ });
+
+ // a setter
+ Comment.path('name').set(function (v) {
+ return capitalize(v);
+ });
+
+ // middleware
+ Comment.pre('save', function (next) {
+ notify(this.get('email'));
+ next();
+ });
+
+Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
+
+## Accessing a Model
+
+Once we define a model through `mongoose.model('ModelName', mySchema)`, we can access it through the same function
+
+ var myModel = mongoose.model('ModelName');
+
+Or just do it all at once
+
+ var MyModel = mongoose.model('ModelName', mySchema);
+
+We can then instantiate it, and save it:
+
+ var instance = new MyModel();
+ instance.my.key = 'hello';
+ instance.save(function (err) {
+ //
+ });
+
+Or we can find documents from the same collection
+
+ MyModel.find({}, function (err, docs) {
+ // docs.forEach
+ });
+
+You can also `findOne`, `findById`, `update`, etc. For more details check out [this link](http://mongoosejs.com/docs/finding-documents.html).
+
+**Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this case access your model through the connection you created:
+
+ var conn = mongoose.createConnection('your connection string');
+ var MyModel = conn.model('ModelName', schema);
+ var m = new MyModel;
+ m.save() // works
+
+ vs
+
+ var conn = mongoose.createConnection('your connection string');
+ var MyModel = mongoose.model('ModelName', schema);
+ var m = new MyModel;
+ m.save() // does not work b/c the default connection object was never connected
+
+## Embedded Documents
+
+In the first example snippet, we defined a key in the Schema that looks like:
+
+ comments: [Comments]
+
+Where `Comments` is a `Schema` we created. This means that creating embedded documents is as simple as:
+
+ // retrieve my model
+ var BlogPost = mongoose.model('BlogPost');
+
+ // create a blog post
+ var post = new BlogPost();
+
+ // create a comment
+ post.comments.push({ title: 'My comment' });
+
+ post.save(function (err) {
+ if (!err) console.log('Success!');
+ });
+
+The same goes for removing them:
+
+ BlogPost.findById(myId, function (err, post) {
+ if (!err) {
+ post.comments[0].remove();
+ post.save(function (err) {
+ // do something
+ });
+ }
+ });
+
+Embedded documents enjoy all the same features as your models. Defaults, validators, middleware. Whenever an error occurs, it's bubbled to the `save()` error callback, so error handling is a snap!
+
+Mongoose interacts with your embedded documents in arrays _atomically_, out of the box.
+
+## Middleware
+
+Middleware is one of the most exciting features about Mongoose. Middleware takes away all the pain of nested callbacks.
+
+Middleware are defined at the Schema level and are applied for the methods `init` (when a document is initialized with data from MongoDB), `save` (when a document or embedded document is saved).
+
+There's two types of middleware:
+
+- Serial
+ Serial middleware are defined like:
+
+ .pre(method, function (next, methodArg1, methodArg2, ...) {
+ // ...
+ })
+
+ They're executed one after the other, when each middleware calls `next`.
+
+ You can also intercept the `method`'s incoming arguments via your middleware -- notice `methodArg1`, `methodArg2`, etc in the `pre` definition above. See section "Intercepting and mutating method arguments" below.
+
+
+- Parallel
+ Parallel middleware offer more fine-grained flow control, and are defined like:
+
+ .pre(method, true, function (next, done, methodArg1, methodArg2) {
+ // ...
+ })
+
+ Parallel middleware can `next()` immediately, but the final argument will be called when all the parallel middleware have called `done()`.
+
+### Error handling
+
+If any middleware calls `next` or `done` with an `Error` instance, the flow is interrupted, and the error is passed to the function passed as an argument.
+
+For example:
+
+ schema.pre('save', function (next) {
+ // something goes wrong
+ next(new Error('something went wrong'));
+ });
+
+ // later...
+
+ myModel.save(function (err) {
+ // err can come from a middleware
+ });
+
+### Intercepting and mutating method arguments
+
+You can intercept method arguments via middleware.
+
+For example, this would allow you to broadcast changes about your Documents every time someone `set`s a path in your Document to a new value:
+
+ schema.pre('set', function (next, path, val, typel) {
+ // `this` is the current Document
+ this.emit('set', path, val);
+
+ // Pass control to the next pre
+ next();
+ });
+
+Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments. To do so, just pass the new values to `next`:
+
+ .pre(method, function firstPre (next, methodArg1, methodArg2) {
+ // Mutate methodArg1
+ next("altered-" + methodArg1.toString(), methodArg2);
+ })
+
+ // pre declaration is chainable
+ .pre(method, function secondPre (next, methodArg1, methodArg2) {
+ console.log(methodArg1);
+ // => 'altered-originalValOfMethodArg1'
+
+ console.log(methodArg2);
+ // => 'originalValOfMethodArg2'
+
+ // Passing no arguments to `next` automatically passes along the current argument values
+ // i.e., the following `next()` is equivalent to `next(methodArg1, methodArg2)`
+ // and also equivalent to, with the example method arg
+ // values, `next('altered-originalValOfMethodArg1', 'originalValOfMethodArg2')`
+ next();
+ })
+
+### Schema gotcha
+
+`type`, when used in a schema has special meaning within Mongoose. If your schema requires using `type` as a nested property you must use object notation:
+
+ new Schema({
+ broken: { type: Boolean }
+ , asset : {
+ name: String
+ , type: String // uh oh, it broke. asset will be interpreted as String
+ }
+ });
+
+ new Schema({
+ works: { type: Boolean }
+ , asset : {
+ name: String
+ , type: { type: String } // works. asset is an object with a type property
+ }
+ });
+
+## API docs
+
+You can find the [Dox](http://github.com/visionmedia/dox) generated API docs [here](http://mongoosejs.com/docs/api.html).
+
+## Getting support
+
+Please subscribe to the Google Groups [mailing list](http://groups.google.com/group/mongoose-orm).
+
+Join #mongoosejs on freenode.
+
+## Driver access
+
+The driver being used defaults to [node-mongodb-native](https://github.com/christkv/node-mongodb-native) and is directly accessible through `YourModel.collection`. **Note**: using the driver directly bypasses all Mongoose power-tools like validation, getters, setters, hooks, etc.
+
+## Mongoose Plugins
+
+The following plugins are currently available for use with mongoose:
+
+- [mongoose-types](https://github.com/bnoguchi/mongoose-types) - Adds
+ several additional types (e.g., Email) that you can use in your
+ Schema declarations
+- [mongoose-auth](https://github.com/bnoguchi/mongoose-auth) - A drop in
+ solution for your auth needs. Currently supports Password, Facebook,
+ Twitter, Github, and more.
+- [mongoose-joins](https://github.com/goulash1971/mongoose-joins) - Adds simple join support
+- [mongoose-dbref](https://github.com/goulash1971/mongoose-dbref) - An alternative DBRef option
+- [mongoose-flatmatcher](https://github.com/marksweiss/mongoose-flatmatcher) - A query pre-processor that maps flat name/value pairs to schemas
+- [mongoose-ttl](https://github.com/aheckmann/mongoose-ttl) - TTL support
+- [mongoose-keywordize](https://github.com/aheckmann/mongoose-keywordize) - auto keywords generation
+
+## Contributing to Mongoose
+
+### Cloning the repository
+
+Make a fork of `mongoose`, then clone it in your computer. The `v2.x` branch contains the current stable release, and the `master` branch the next upcoming major release.
+
+### Guidelines
+
+- Please write inline documentation for new methods or class members.
+- Please write tests and make sure your tests pass.
+- Before starting to write code, look for existing tickets or create one for your specific issue (unless you're addressing something that's clearly broken). That way you avoid working on something that might not be of interest or that has been addressed already in a different branch.
+
+## Credits
+
+- Guillermo Rauch - guillermo@learnboost.com - [Guille](http://github.com/guille)
+- Nathan White - [nw](http://github.com/nw/)
+- Brian Noguchi - [bnoguchi](https://github.com/bnoguchi)
+- Aaron Heckmann - [aheckmann](https://github.com/aheckmann)
+
+## License
+
+Copyright (c) 2010-2012 LearnBoost &lt;dev@learnboost.com&gt;
+
+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.
diff --git a/node_modules/mongoose/benchmarks/clone.js b/node_modules/mongoose/benchmarks/clone.js
new file mode 100644
index 0000000..45aa6a7
--- /dev/null
+++ b/node_modules/mongoose/benchmarks/clone.js
@@ -0,0 +1,60 @@
+
+var mongoose = require('../')
+ , utils = require('../lib/utils')
+ , clone = utils.clone
+ , Schema = mongoose.Schema
+
+var DocSchema = new Schema({
+ title: String
+});
+
+var AllSchema = new Schema({
+ string: { type: String, required: true }
+ , number: { type: Number, min: 10 }
+ , date : Date
+ , bool : Boolean
+ , buffer: Buffer
+ , objectid: Schema.ObjectId
+ , array : Array
+ , strings: [String]
+ , numbers: [Number]
+ , dates : [Date]
+ , bools : [Boolean]
+ , buffers: [Buffer]
+ , objectids: [Schema.ObjectId]
+ , docs : { type: [DocSchema], validate: function () { return true }}
+ , s: { nest: String }
+});
+
+var A = mongoose.model('A', AllSchema);
+var a = new A({
+ string: "hello world"
+ , number: 444848484
+ , date: new Date
+ , bool: true
+ , buffer: new Buffer(0)
+ , objectid: new mongoose.Types.ObjectId()
+ , array: [4,{},[],"asdfa"]
+ , strings: ["one","two","three","four"]
+ , numbers:[72,6493,83984643,348282.55]
+ , dates:[new Date, new Date, new Date]
+ , bools:[true, false, false, true, true]
+ , buffers: [new Buffer([33]), new Buffer([12])]
+ , objectids: [new mongoose.Types.ObjectId]
+ , docs: [ {title: "yo"}, {title:"nowafasdi0fas asjkdfla fa" }]
+ , s: { nest: 'hello there everyone!' }
+});
+
+var start = new Date;
+var total = 100000;
+var i = total;
+
+for (var i = 0, len = total; i < len; ++i) {
+ a.toObject({ depopulate: true });
+}
+
+var time= (new Date - start)/1000;
+console.error('took %d seconds for %d docs (%d dps)', time, total, total/time);
+var used = process.memoryUsage();
+
+// --trace-opt --trace-deopt --trace-bailout
diff --git a/node_modules/mongoose/benchmarks/index.js b/node_modules/mongoose/benchmarks/index.js
new file mode 100644
index 0000000..4695113
--- /dev/null
+++ b/node_modules/mongoose/benchmarks/index.js
@@ -0,0 +1,128 @@
+Error.stackTraceLimit = Infinity;
+var mongoose = require('../')
+ , Schema = mongoose.Schema;
+
+var DocSchema = new Schema({
+ title: String
+});
+
+var AllSchema = new Schema({
+ string: String
+ , number: Number
+ , date : Date
+ , bool : Boolean
+ , buffer: Buffer
+ , objectid: Schema.ObjectId
+ , array : Array
+ , strings: [String]
+ , numbers: [Number]
+ , dates : [Date]
+ , bools : [Boolean]
+ , buffers: [Buffer]
+ , objectids: [Schema.ObjectId]
+ , docs : [DocSchema]
+});
+
+var A = mongoose.model('A', AllSchema);
+
+// bench the normal way
+// the try building the doc into the document prototype
+// and using inheritance and bench that
+//
+// also, bench using listeners for each subdoc vs one
+// listener that knows about all subdocs and notifies
+// them.
+
+function run (label, fn) {
+ console.error('running %s', label);
+ var started = process.memoryUsage();
+ var start = new Date;
+ var total = 10000;
+ var i = total;
+ while (i--) {
+ a = fn();
+ if (i%2)
+ a.toObject({ depopulate: true });
+ else
+ a._delta();
+ }
+ var time = (new Date - start)/1000;
+ console.error(label + ' took %d seconds for %d docs (%d dps)', time, total, total/time);
+ var used = process.memoryUsage();
+ console.error(((used.vsize - started.vsize) / 1048576)+' MB');
+}
+
+run('string', function () {
+ return new A({
+ string: "hello world"
+ });
+})
+run('number', function () {
+ return new A({
+ number: 444848484
+ });
+})
+run('date', function () {
+ return new A({
+ date: new Date
+ });
+})
+run('bool', function () {
+ return new A({
+ bool: true
+ });
+})
+run('buffer', function () {
+ return new A({
+ buffer: new Buffer(0)
+ });
+})
+run('objectid', function () {
+ return new A({
+ objectid: new mongoose.Types.ObjectId()
+ });
+})
+run('array of mixed', function () {
+ return new A({
+ array: [4,{},[],"asdfa"]
+ });
+})
+run('array of strings', function () {
+ return new A({
+ strings: ["one","two","three","four"]
+ });
+})
+run('array of numbers', function () {
+ return new A({
+ numbers:[72,6493,83984643,348282.55]
+ });
+})
+run('array of dates', function () {
+ return new A({
+ dates:[new Date, new Date, new Date]
+ });
+})
+run('array of bools', function () {
+ return new A({
+ bools:[true, false, false, true, true]
+ });
+})
+run('array of buffers', function () {
+ return new A({
+ buffers: [new Buffer([33]), new Buffer([12])]
+ });
+})
+run('array of objectids', function () {
+ return new A({
+ objectids: [new mongoose.Types.ObjectId]
+ });
+})
+run('array of docs', function () {
+ return new A({
+ docs: [ {title: "yo"}, {title:"nowafasdi0fas asjkdfla fa" }]
+ });
+})
+
+//console.error(a.toObject({depopulate:true}));
+
+// --trace-opt --trace-deopt --trace-bailout
diff --git a/node_modules/mongoose/benchmarks/mem.js b/node_modules/mongoose/benchmarks/mem.js
new file mode 100644
index 0000000..8ce0cfe
--- /dev/null
+++ b/node_modules/mongoose/benchmarks/mem.js
@@ -0,0 +1,149 @@
+
+var mongoose = require('../')
+ , Schema = mongoose.Schema;
+
+var db = mongoose.connect('localhost', 'testing_bench');
+
+var DocSchema = new Schema({
+ title: String
+});
+
+var AllSchema = new Schema({
+ string: { type: String, required: true }
+ , number: { type: Number, min: 10 }
+ , date : Date
+ , bool : Boolean
+ , buffer: Buffer
+ , objectid: Schema.ObjectId
+ , array : Array
+ , strings: [String]
+ , numbers: [Number]
+ , dates : [Date]
+ , bools : [Boolean]
+ , buffers: [Buffer]
+ , objectids: [Schema.ObjectId]
+ , docs : { type: [DocSchema], validate: function () { return true }}
+ , s: { nest: String }
+});
+
+var A = mongoose.model('A', AllSchema);
+
+var methods = [];
+methods.push(function (a, cb) {
+ A.findOne({ _id: a._id }, cb);
+}); // 2 MB
+methods.push(function (a, cb) {
+ A.find({ _id: a._id, bool: a.bool }, cb);
+}); // 3.8 MB
+methods.push(function (a, cb) {
+ A.findById(a._id, cb);
+}); // 4.6 MB
+methods.push(function (a, cb) {
+ A.where('number', a.number).sort('_id', -1).limit(10).run(cb)
+}); // 4.8 MB
+methods.push(function (a, cb) {
+ A.where('date', a.date).select('string').limit(10).run(cb)
+}); // 3.5 mb
+methods.push(function (a, cb) {
+ A.where('date', a.date).select('string', 'bool').asc('date').limit(10).run(cb)
+}); // 3.5 MB
+methods.push(function (a, cb) {
+ A.find('date', a.date).where('array').$in(3).limit(10).run(cb)
+}); // 1.82 MB
+methods.push(function (a, cb) {
+ A.update({ _id: a._id }, { $addToset: { array: "heeeeello" }}, cb);
+}); // 3.32 MB
+methods.push(function (a, cb) {
+ A.remove({ _id: a._id }, cb);
+}); // 3.32 MB
+methods.push(function (a, cb) {
+ A.find().where('objectids').exists().only('dates').limit(10).exec(cb);
+}); // 3.32 MB
+methods.push(function (a, cb) {
+ A.count({ strings: a.strings[2], number: a.number }, cb);
+}); // 3.32 MB
+methods.push(function (a, cb) {
+ a.string= "asdfaf";
+ a.number = 38383838;
+ a.date= new Date;
+ a.bool = false;
+ a.array.push(3);
+ a.dates.push(new Date);
+ a.bools.$pushAll([true, false]);
+ a.docs.$addToSet({ title: 'woot' });
+ a.strings.remove("three");
+ a.numbers.$pull(72);
+ a.objectids.$pop();
+ a.docs.$pullAll(a.docs);
+ a.s.nest = "aooooooga";
+
+ if (i%2)
+ a.toObject({ depopulate: true });
+ else
+ a._delta();
+
+ cb();
+});
+
+var started = process.memoryUsage();
+var start = new Date;
+var total = 500;
+var i = total;
+
+mongoose.connection.on('open', function () {
+ mongoose.connection.db.dropDatabase(function () {
+
+ ;(function cycle () {
+ if (0 === i--) return done();
+
+ var a = new A({
+ string: "hello world"
+ , number: 444848484
+ , date: new Date
+ , bool: true
+ , buffer: new Buffer(0)
+ , objectid: new mongoose.Types.ObjectId()
+ , array: [4,{},[],"asdfa"]
+ , strings: ["one","two","three","four"]
+ , numbers:[72,6493,83984643,348282.55]
+ , dates:[new Date, new Date, new Date]
+ , bools:[true, false, false, true, true]
+ , buffers: [new Buffer([33]), new Buffer([12])]
+ , objectids: [new mongoose.Types.ObjectId]
+ , docs: [ {title: "yo"}, {title:"nowafasdi0fas asjkdfla fa" }]
+ });
+
+ a.save(function (err) {
+ methods[Math.random()*methods.length|0](a, function () {
+ process.nextTick(cycle);
+ })
+ });
+
+ //if (i%2)
+ //a.toObject({ depopulate: true });
+ //else
+ //a._delta();
+
+ if (!(i%50)) {
+ var u = process.memoryUsage();
+ console.error('rss: %d, vsize: %d, heapTotal: %d, heapUsed: %d',
+ u.rss, u.vsize, u.heapTotal, u.heapUsed);
+ }
+ })()
+
+ function done () {
+ var time= (new Date - start)/1000;
+ console.error('took %d seconds for %d docs (%d dps)', time, total, total/time);
+ var used = process.memoryUsage();
+ console.error(((used.vsize - started.vsize) / 1048576)+' MB');
+
+ //console.error(a.toObject({depopulate:true}));
+
+ mongoose.connection.db.dropDatabase(function () {
+ mongoose.connection.close();
+ });
+ }
+
+ // --trace-opt --trace-deopt --trace-bailout
+ })
+})
diff --git a/node_modules/mongoose/docs/defaults.md b/node_modules/mongoose/docs/defaults.md
new file mode 100644
index 0000000..0497787
--- /dev/null
+++ b/node_modules/mongoose/docs/defaults.md
@@ -0,0 +1,29 @@
+
+Defaults
+========
+
+Each `SchemaType` that you define \(you can read more about them in the [model
+definition chapter](/docs/model-definition.html) \) can have a default value.
+
+Default values are applied when the document skeleton is constructed. This
+means that if you create a new document (`new MyModel`) or if you find an
+existing document (`MyModel.findById`), both will have defaults
+provided that a certain key is missing.
+
+## Definition
+
+You can define a default with a function:
+
+ new Schema({
+ date: { type: Date, default: Date.now }
+ })
+
+or a value:
+
+ new Schema({
+ date: { type: Date, default: '12/10/1990' }
+ })
+
+Notice that defaults are automatically casted. In both cases, the defaults will
+become actual `Date` objects, but we're passing a timestamp first, and a string
+date second.
diff --git a/node_modules/mongoose/docs/embedded-documents.md b/node_modules/mongoose/docs/embedded-documents.md
new file mode 100644
index 0000000..10b6aae
--- /dev/null
+++ b/node_modules/mongoose/docs/embedded-documents.md
@@ -0,0 +1,79 @@
+
+Embedded Documents
+==================
+
+Embedded documents are documents with schemas of their own that are part of
+other documents (as items within an array).
+
+Embedded documents enjoy all the same features as your models. Defaults,
+validators, middleware. Whenever an error occurs, it's bubbled to the `save()`
+error callback, so error handling is a snap!
+
+Mongoose interacts with your embedded documents in arrays _atomically_, out of
+the box.
+
+## Definition and initialization
+
+When you define a Schema like this:
+
+ var Comments = new Schema({
+ title : String
+ , body : String
+ , date : Date
+ });
+
+ var BlogPost = new Schema({
+ author : ObjectId
+ , title : String
+ , body : String
+ , date : Date
+ , comments : [Comments]
+ , meta : {
+ votes : Number
+ , favs : Number
+ }
+ });
+
+ mongoose.model('BlogPost', BlogPost);
+
+The `comments` key of your `BlogPost` documents will then be an instance of
+`DocumentArray`. This is a special subclassed `Array` that can deal with
+casting, and has special methods to work with embedded documents.
+
+## Adding an embedded document to an array
+
+ // retrieve my model
+ var BlogPost = mongoose.model('BlogPost');
+
+ // create a blog post
+ var post = new BlogPost();
+
+ // create a comment
+ post.comments.push({ title: 'My comment' });
+
+ post.save(function (err) {
+ if (!err) console.log('Success!');
+ });
+
+## Removing an embedded document
+
+ BlogPost.findById(myId, function (err, post) {
+ if (!err) {
+ post.comments[0].remove();
+ post.save(function (err) {
+ // do something
+ });
+ }
+ });
+
+## Finding an embedded document by id
+
+`DocumentArray`s have an special method `id` that filters your embedded
+documents by their `_id` property (each embedded document gets one):
+
+Consider the following snippet:
+
+ post.comments.id(my_id).remove();
+ post.save(function (err) {
+ // embedded comment with id `my_id` removed!
+ });
diff --git a/node_modules/mongoose/docs/errors.md b/node_modules/mongoose/docs/errors.md
new file mode 100644
index 0000000..a1852ef
--- /dev/null
+++ b/node_modules/mongoose/docs/errors.md
@@ -0,0 +1,54 @@
+
+Error handling
+==============
+
+Errors returned after failed validation contain an `errors` object
+holding the actual ValidatorErrors. Each ValidatorError has a `type` and `path` property
+providing us with a little more error handling flexibility.
+
+ var ToySchema = new Schema({
+ color: String
+ , name: String
+ });
+
+ var Toy = db.model('Toy', ToySchema);
+
+ Toy.schema.path('name').validate(function (value) {
+ return /blue|green|white|red|orange|periwinkel/i.test(value);
+ }, 'Invalid color');
+
+ var toy = new Toy({ color: 'grease'});
+
+ toy.save(function (err) {
+ // previous behavior (v1x):
+
+ console.log(err.errors.color)
+ // prints 'Validator "Invalid color" failed for path color'
+
+ // new v2x behavior - err.errors.color is a ValidatorError object
+
+ console.log(err.errors.color.message)
+ // prints 'Validator "Invalid color" failed for path color'
+
+ // you can get v1 behavior back by casting error.color toString
+
+ console.log(String(err.errors.color))
+ // prints 'Validator "Invalid color" failed for path color'
+
+ console.log(err.errors.color.type);
+ // prints "Invalid color"
+
+ console.log(err.errors.color.path)
+ // prints "color"
+
+ console.log(err.name)
+ // prints "ValidationError"
+
+ console.log(err.message)
+ // prints "Validation failed"
+ });
+
+BTW, the `err.errors` object is also available on the model instance.
+
+ toy.errors.color.message === err.errors.color.message
+
diff --git a/node_modules/mongoose/docs/finding-documents.md b/node_modules/mongoose/docs/finding-documents.md
new file mode 100644
index 0000000..db6e25d
--- /dev/null
+++ b/node_modules/mongoose/docs/finding-documents.md
@@ -0,0 +1,124 @@
+
+Querying
+=================
+
+Documents can be retrieved through `find`, `findOne` and `findById`. These
+methods are executed on your `Model`s.
+
+## Model.find
+
+ Model.find(query, fields, options, callback)
+
+ // fields and options can be omitted
+
+### Simple query:
+
+ Model.find({ 'some.value': 5 }, function (err, docs) {
+ // docs is an array
+ });
+
+### Retrieving only certain fields
+
+ Model.find({}, ['first', 'last'], function (err, docs) {
+ // docs is an array of partially-`init`d documents
+ // defaults are still applied and will be "populated"
+ })
+
+## Model.findOne
+
+Same as `Model#find`, but only receives a single document as second parameter:
+
+ Model.findOne({ age: 5}, function (err, doc){
+ // doc is a Document
+ });
+
+## Model.findById
+
+Same as `findOne`, but receives a value to search a document by their `_id`
+key. This value is subject to casting, so it can be a hex string or a proper
+ObjectId.
+
+ Model.findById(obj._id, function (err, doc){
+ // doc is a Document
+ });
+
+## Model.count
+
+Counts the number of documents matching `conditions`.
+
+ Model.count(conditions, callback);
+
+## Model.remove
+
+Removes documents matching `conditions`.
+
+ Model.remove(conditions, callback);
+
+## Model.distinct
+
+Finds distinct values of `field` for documents matching `conditions`.
+
+ Model.distinct(field, conditions, callback);
+
+## Model.where
+
+Creates a Query for this model.
+Handy when expressing complex directives.
+
+ Model
+ .where('age').gte(25)
+ .where('tags').in(['movie', 'music', 'art'])
+ .select('name', 'age', 'tags')
+ .skip(20)
+ .limit(10)
+ .asc('age')
+ .slaveOk()
+ .hint({ age: 1, name: 1 })
+ .run(callback);
+
+## Model.$where
+
+Sometimes you need to query for things in mongodb using a JavaScript
+expression. You can do so via find({$where: javascript}), or you can
+use the mongoose shortcut method $where via a Query chain or from
+your mongoose Model.
+
+ Model.$where('this.firstname === this.lastname').exec(callback)
+
+## Model.update
+
+Updates all documents matching `conditions` using the `update` clause. All
+`update` values are casted to their appropriate types before being sent.
+
+ var conditions = { name: 'borne' }
+ , update = { $inc: { visits: 1 }}
+ , options = { multi: true };
+
+ Model.update(conditions, update, options, callback)
+
+Note: for backwards compatibility, all top-level `update` keys that are
+not $atomic operation names are treated as `$set` operations. Example:
+
+ var query = { name: 'borne' };
+ Model.update(query, { name: 'jason borne' }, options, callback)
+
+ // is sent as
+
+ Model.update(query, { $set: { name: 'jason borne' }}, options, callback)
+
+## Query
+
+Each of these methods returns a [Query](https://github.com/LearnBoost/mongoose/blob/master/lib/query.js).
+If you don't pass a callback to these methods, the Query can be continued to be
+modified (such as adding options, fields, etc), before it's `exec`d.
+
+ var query = Model.find({});
+
+ query.where('field', 5);
+ query.limit(5);
+ query.skip(100);
+
+ query.exec(function (err, docs) {
+ // called when the `query.complete` or `query.error` are called
+ // internally
+ });
diff --git a/node_modules/mongoose/docs/getters-setters.md b/node_modules/mongoose/docs/getters-setters.md
new file mode 100644
index 0000000..7cf7b94
--- /dev/null
+++ b/node_modules/mongoose/docs/getters-setters.md
@@ -0,0 +1,52 @@
+Getters and Setters
+====================
+
+Getters and setters help you change how you get and set the attributes defined by the keys and values in the underlying raw document.
+
+## Setters
+
+Setters allow you to transform the mongoose document's data before it gets to the raw mongodb document and is set as a value on an actual key.
+
+Suppose you are implementing user registration for a website. User provide an email and password, which gets saved to mongodb. The email is a string that you will want to normalize to lower case, in order to avoid one email having more than one account -- e.g., otherwise, avenue@q.com can be registered for 2 accounts via avenue@q.com and AvEnUe@Q.CoM.
+
+You can set up email lower case normalization easily via a Mongoose setter. Note in the following snippet that setters (and also getters) are defined in the `Schema`:
+
+ function toLower (v) {
+ return v.toLowerCase();
+ }
+
+ var UserSchema = new Schema({
+ email: { type: String, set: toLower }
+ });
+
+ var User = mongoose.model('User', UserSchema);
+ var user = new User({email: 'AVENUE@Q.COM'});
+
+ console.log(user.email); // 'avenue@q.com'
+
+
+As you can see above, setters allow you to transform the data before it gets to the raw mongodb document and is set as a value on an actual key.
+
+## Getters
+
+Getters allow you to transform the representation of the data as it travels from the raw mongodb document to the value that you see.
+
+Suppose you are storing credit card numbers and you want to hide everything except the last 4 digits to the mongoose user. You can do so by defining a getter in the following way (again, notice that getters are defined in the `Schema`):
+
+ function obfuscate (cc) {
+ return '****-****-****-' + cc.slice(cc.length-4, cc.length);
+ }
+
+ var AccountSchema = new Schema({
+ creditCardNumber: { type: String, get: obfuscate }
+ });
+
+ var Account = mongoose.model('Account', AccountSchema);
+
+ Account.findById( someId, function (err, found) {
+ console.log(found.creditCardNumber); // '****-****-****-1234'
+ });
+
+## Summary
+
+Setters are intended to modify the underlying raw data. Getters are intended to transform (but not modify at the raw data level) the underlying raw data into something that the user expects to see. They are both defined in the `Schema` definition.
diff --git a/node_modules/mongoose/docs/indexes.md b/node_modules/mongoose/docs/indexes.md
new file mode 100644
index 0000000..f294318
--- /dev/null
+++ b/node_modules/mongoose/docs/indexes.md
@@ -0,0 +1,43 @@
+
+Indexes
+=======
+
+Indexes are defined through `ensureIndex` every time a model is compiled for a
+certain connection / database. This means that indexes will only be ensured
+once during the lifetime of your app.
+
+## Definition
+
+Regular indexes:
+
+ var User = new Schema({
+ name: { type: String, index: true }
+ })
+
+[Sparse](http://www.mongodb.org/display/DOCS/Indexes#Indexes-SparseIndexes) indexes:
+
+ var User = new Schema({
+ name: { type: String, sparse: true }
+ })
+
+Unique indexes:
+
+ var User = new Schema({
+ name: { type: String, unique: true }
+ })
+
+ // or
+
+ var User = new Schema({
+ name: { type: String, index: { unique: true } }
+ })
+
+Unique sparse indexes:
+
+ var User = new Schema({
+ name: { type: String, unique: true, sparse: true }
+ })
+
+Compound indexes are defined on the `Schema` itself.
+
+ User.index({ first: 1, last: -1 }, { unique: true })
diff --git a/node_modules/mongoose/docs/methods-statics.md b/node_modules/mongoose/docs/methods-statics.md
new file mode 100644
index 0000000..27b5902
--- /dev/null
+++ b/node_modules/mongoose/docs/methods-statics.md
@@ -0,0 +1,55 @@
+Methods and Statics
+====================
+
+Each `Schema` can define instance and static methods for its model.
+
+## Methods
+
+Methods are easy to define:
+
+ var AnimalSchema = new Schema({
+ name: String
+ , type: String
+ });
+
+ AnimalSchema.methods.findSimilarType = function findSimilarType (cb) {
+ return this.find({ type: this.type }, cb);
+ };
+
+Now when we have an instance of `Animal` we can call our `findSimilarType` method and
+find all animals with a matching `type`.
+
+ var Animal = mongoose.model('Animal', AnimalSchema);
+ var dog = new Animal({ name: 'Rover', type: 'dog' });
+
+ dog.findSimilarType(function (err, dogs) {
+ if (err) return ...
+ dogs.forEach(..);
+ })
+
+Note that we return what `.find()` returns in our method. The advantages are two-fold.
+First, by passing `cb` into `find` we are making it optional b/c `find` called
+without a callback will not run the query. Secondly, `this.find`, `this.where`,
+and other Model methods return instances of [Query](/docs/finding-documents.html)
+which allow us to further utilize its expressive capabilities.
+
+ dog
+ .findSimilarType()
+ .where('name': /rover/i)
+ .limit(20)
+ .run(function (err, rovers) {
+ if (err) ...
+ })
+
+## Statics
+
+Statics are pretty much the same as methods but allow for defining functions that
+exist directly on your Model.
+
+ AnimalSchema.statics.search = function search (name, cb) {
+ return this.where('name', new RegExp(name, 'i')).run(cb);
+ }
+
+ Animal.search('Rover', function (err) {
+ if (err) ...
+ })
diff --git a/node_modules/mongoose/docs/middleware.md b/node_modules/mongoose/docs/middleware.md
new file mode 100644
index 0000000..947006d
--- /dev/null
+++ b/node_modules/mongoose/docs/middleware.md
@@ -0,0 +1,61 @@
+
+Middleware
+==========
+
+Middleware are defined at the Schema level and are applied when the methods
+`init` (when a document is initialized with data from MongoDB), `save`, and
+`remove` are called on a document instance.
+
+There are two types of middleware, serial and parallel.
+
+Serial middleware are defined like:
+
+ schema.pre('save', function (next) {
+ // ...
+ })
+
+They're executed one after the other, when each middleware calls `next`.
+
+Parallel middleware offer more fine-grained flow control, and are defined
+like
+
+ schema.pre('remove', true, function (next, done) {
+ // ...
+ })
+
+Parallel middleware can `next()` immediately, but the final argument will be
+called when all the parallel middleware have called `done()`.
+
+## Use cases
+
+Middleware are useful for:
+
+- Complex validation
+- Removing dependent documents when a certain document is removed (eg:
+removing a user removes all his blogposts)
+- Asynchronous defaults
+- Asynchronous tasks that a certain action triggers. For example:
+ - Triggering custom events
+ - Creating notifications
+ - Emails
+
+and many other things. They're specially useful for atomizing model logic
+and avoiding nested blocks of async code.
+
+## Error handling
+
+If any middleware calls `next` or `done` with an `Error` instance, the flow is
+interrupted, and the error is passed to the callback.
+
+For example:
+
+ schema.pre('save', function (next) {
+ // something goes wrong
+ next(new Error('something went wrong'));
+ });
+
+ // later...
+
+ myModel.save(function (err) {
+ // err can come from a middleware
+ });
diff --git a/node_modules/mongoose/docs/migration-guide.md b/node_modules/mongoose/docs/migration-guide.md
new file mode 100644
index 0000000..214bd4e
--- /dev/null
+++ b/node_modules/mongoose/docs/migration-guide.md
@@ -0,0 +1,148 @@
+
+Migrating from v1.x to 2.x
+==========================
+
+Migrating from __v1.x__ to __2.x__ brings with it a few changes to be aware of.
+
+## Auto-reconnect
+
+Previously the `auto_reconnect` option of the node-mongodb-driver
+defaulted to false. It now defaults to true so if your connection drops
+while your app is running the driver will continue retrying until it
+can connect again.
+
+## Private props
+
+Several internal instance props have had name changes so its more obvious that
+they are not intended for public use. Namely `instance.doc` has changed
+to `instance._doc` since it contains the structure Mongoose relies on
+to operate properly and should only be manipulated with caution.
+
+Here are the relavent changes:
+
+ var thing = new Thing;
+
+ thing.doc -> thing._doc
+ thing.activePaths -> thing._activePaths
+ thing.saveError -> thing._saveError
+ thing.validationError -> thing._validationError
+
+## Circular refs in getters
+
+Previously Mongoose exibited very odd behavior with getters:
+
+ toy.color.color.color.color ... // actually worked!
+
+Obviously this was wrong and has now been fixed.
+
+ toy.color.color // undefined
+
+## Getter / Setter scope
+
+Nested getter/setter scopes were set incorrectly since version 1.7 or so.
+This has been fixed. In your getter/setter, `this` now properly refers
+to the instance.
+
+ var SongSchema = new Schema({
+ title: String
+ , detail: {
+ format: String
+ }
+ });
+
+ SongSchema.path('detail.format').get(function () {
+ console.log(this !== this.detail) // true, used to be false
+ });
+
+You may not have noticed this bug since the circular getters previously
+masked (_mostly_) this bad behavior.
+
+## Setters application
+
+Setters are no longer applied when the doc returns from the db (bug). It
+caused problems for folks trying to use setters for passwords / salts
+resulting in doubly hashed passwords after queries.
+
+ UserSchema.path('password').set(function (val) {
+ // now only runs when you change `user.password`
+ // not when the doc returns from the db
+ });
+
+## Query#bind
+
+If you were using the `Query` object directly and calling its `bind`
+method, the v1.x behavior cloned the query and returned the
+new one. This is no longer the case. The query is now simply
+bound and returns itself.
+
+## Multiple collection support removed
+
+In 1.x Mongoose had support for multiple collection names per model. This
+was an edge case and support for it has been removed.
+
+## Compat.js removed
+
+Backward compatibility with verions 0.x has been removed.
+
+ require('mongoose').compat = true // no longer does anything
+
+## Utils.erase removed
+
+We removed utils.erase since it was unused in the project. If you were
+using it you'll need to copy it from the 1.x branch into your own.
+
+## Error handling
+
+Previously, the error returned after failed validation contained an `errors`
+object which was a hash of path keys to error message values.
+Now the Error returned is more helpful. Instead of the `errors`
+object containing string values it holds the actual
+ValidatorError. Each ValidatorError has a `type` and `path` property
+providing us with a little more error handling flexibility.
+
+ var ToySchema = new Schema({
+ color: String
+ , name: String
+ });
+
+ var Toy = db.model('Toy', ToySchema);
+
+ Toy.schema.path('name').validate(function (value) {
+ return /blue|green|white|red|orange|periwinkel/i.test(value);
+ }, 'Invalid color');
+
+ var toy = new Toy({ color: 'grease'});
+
+ toy.save(function (err) {
+ // previous behavior (v1x):
+
+ console.log(err.errors.color)
+ // prints 'Validator "Invalid color" failed for path color'
+
+ // new v2x behavior - err.errors.color is a ValidatorError object
+
+ console.log(err.errors.color.message)
+ // prints 'Validator "Invalid color" failed for path color'
+
+ // you can get v1 behavior back by casting error.color toString
+
+ console.log(String(err.errors.color))
+ // prints 'Validator "Invalid color" failed for path color'
+
+ console.log(err.errors.color.type);
+ // prints "Invalid color"
+
+ console.log(err.errors.color.path)
+ // prints "color"
+
+ console.log(err.name)
+ // prints "ValidationError"
+
+ console.log(err.message)
+ // prints "Validation failed"
+ });
+
+BTW, the `err.errors` object is also available on the model instance.
+
+ toy.errors.color.message === err.errors.color.message
+
diff --git a/node_modules/mongoose/docs/model-definition.md b/node_modules/mongoose/docs/model-definition.md
new file mode 100644
index 0000000..69700ff
--- /dev/null
+++ b/node_modules/mongoose/docs/model-definition.md
@@ -0,0 +1,142 @@
+Defining a model
+================
+
+Models are defined by passing a `Schema` instance to `mongoose.model`.
+
+ mongoose.model('MyModel', mySchema);
+ // mySchema is <a Schema>
+
+You can easily access the `Schema` constructor from the `mongoose` singleton:
+
+ var mongoose = require('mongoose')
+ , Schema = mongoose.Schema;
+
+ var mySchema = new Schema({
+ // my props
+ });
+
+Models are then accessed from `mongoose` if you want to use a single
+connection:
+
+ // connect the `mongoose` instance
+ mongoose.connect('mongodb://host/db');
+
+ var BlogPost = mongoose.model('BlogPost');
+
+Or from a `Connection` instance if you want to use multiple
+databases/connections:
+
+ var db = mongoose.createConnection('mongodb://host/db')
+ , BlogPost = db.model('BlogPost');
+
+**Important**: the actual interaction with the data happens with the `Model`
+that you obtain through `mongoose.model` or `db.model`. That's the object that
+you can instantiate or that you can call `.find()`, `.findOne()`, etc upon.
+Don't confuse schemas and actual models!
+
+## Defining your keys
+
+The `Schema` constructor receives an object representation of your schemas as
+its first parameter. If you want to add more keys later, `Schema#add` provides
+the same functionality.
+
+Your schema is constructed by passing all the
+JavaScript natives that you know (String, Number, Date, Buffer) as well
+as others exclusive to MongoDb (for example `Schema.ObjectId`). For details on all
+SchemaTypes see the [Schema Type chapter](/docs/schematypes.html).
+
+ var ObjectId = Schema.ObjectId;
+
+ var PostSchema = new Schema({
+ owner : ObjectId
+ , title : String
+ , date : Date
+ });
+
+### Defining documents within documents
+
+To define an array of documents that follows a certain schema, make the value
+an array with the schema constructor inside.
+
+For example, let's assume we want to have a collection of comments within a
+blogpost, and we want them to be subject to casting, validation, and other
+functionality provided by models:
+
+ var Comment = new Schema({
+ body : String
+ , date : Date
+ });
+
+ var Post = new Schema({
+ title : String
+ , comments : [Comment]
+ });
+
+This will allow you to interact very easily with subdocuments later on. For
+more information, refer to the chapter on
+[embedded documents](/docs/embedded-documents.html).
+
+### Defining custom options for keys
+
+Each key that you define is internally mapped to a `SchemaType`. Bear in mind, a
+Schema is not something that you interact directly with, but it's a way to
+describe to Mongoose what your want your data to look like, and how you want
+it to behave.
+
+`SchemaType`s take care of validation, casting, defaults, and other general
+options. Some functionality is exclusive to certain types of `SchemaType`s, for
+example only numbers support `min` and `max` values.
+
+In order to customize some of these options directly from the definition of
+your model, set your key to an object with the format `{ type: Type, ... }`.
+
+ var Person = new Schema({
+ title : { type: String, required: true }
+ , age : { type: Number, min: 5, max: 20 }
+ , meta : {
+ likes : [String]
+ , birth : { type: Date, default: Date.now }
+ }
+ });
+
+Those options are functions that are called on each SchemaType.
+If you want to define options later on, you could access a certain key through
+the `path` function:
+
+ Person.path('age').max(400);
+
+ Person.path('meta.birth').set(function (v) {
+ // this is a setter
+ });
+
+ Person.path('title').validate(function (v) {
+ return v.length > 50;
+ });
+
+Some of the options are versatile. `default` takes a `Function` or a value.
+`validate` takes a `Function` or a `RegExp`. More information on these can be
+found in the [Schema Type chapter](/docs/schematypes.html).
+
+## Beyond keys: Middleware
+
+Middleware are special user-defined functions that are called transparently
+when certain native methods are called on `Document` instances (`init`, `save`
+and `remove`).
+
+Let's say that you want to email a certain user when his document changes.
+You'd then define a hook on the User schema like this:
+
+ User.pre('save', function (next) {
+ email(this.email, 'Your record has changed');
+ next();
+ });
+
+More information about the specifics of middleware can be found [here](/docs/middleware.html).
+
+## Instance Methods and Static methods
+
+For details about defining your own custom static and instance methods read [this](/docs/methods-statics.html).
+
+## Plugins
+
+Schemas also support plugins. Read more about it on the [Plugins](/docs/plugins.html) page.
diff --git a/node_modules/mongoose/docs/plugins.md b/node_modules/mongoose/docs/plugins.md
new file mode 100644
index 0000000..2d22a55
--- /dev/null
+++ b/node_modules/mongoose/docs/plugins.md
@@ -0,0 +1,38 @@
+Plugins
+====================
+
+`Schema`s are pluggable, that is, they allow for applying pre-packaged
+capabilities to extend their functionality.
+
+Suppose that we have several collections in our database and want
+to add last-modified functionality to each one. With plugins this
+is easy. Just create a plugin once and apply it to each `Schema`:
+
+ // lastMod.js
+ module.exports = exports = function lastModifiedPlugin (schema, options) {
+ schema.add({ lastMod: Date })
+
+ schema.pre('save', function (next) {
+ this.lastMod = new Date
+ next()
+ })
+
+ if (options && options.index) {
+ schema.path('lastMod').index(options.index)
+ }
+ }
+
+ // in your schema files
+ var lastMod = require('./lastMod');
+
+ var Game = new Schema({ ... });
+
+ Game.plugin(lastMod);
+
+ var Player = new Schema({ ... });
+
+ Player.plugin(lastMod, { index: true });
+
+In the example above we added last-modified functionality to both the Game
+and Player schemas. We also took advantage of options passing supported by
+the `plugin()` method to dynamically define an index on the Player.
diff --git a/node_modules/mongoose/docs/populate.md b/node_modules/mongoose/docs/populate.md
new file mode 100644
index 0000000..d2ad207
--- /dev/null
+++ b/node_modules/mongoose/docs/populate.md
@@ -0,0 +1,166 @@
+Populate - DBRef-like behavior (experimental)
+=============================================
+
+`ObjectIds` can now refer to another document in a
+collection within our database and be populated when
+querying. An example is helpful:
+
+ var mongoose = require('mongoose')
+ , Schema = mongoose.Schema
+
+ var PersonSchema = new Schema({
+ name : String
+ , age : Number
+ , stories : [{ type: Schema.ObjectId, ref: 'Story' }]
+ });
+
+ var StorySchema = new Schema({
+ _creator : { type: Schema.ObjectId, ref: 'Person' }
+ , title : String
+ , fans : [{ type: Schema.ObjectId, ref: 'Person' }]
+ });
+
+ var Story = mongoose.model('Story', StorySchema);
+ var Person = mongoose.model('Person', PersonSchema);
+
+So far we've created two models. Our `Person` model has it's `stories` field
+set to an array of `ObjectId`s. The `ref` option is what tells Mongoose in which
+model to look, in our case the `Story` model. All `_id`s we
+store here must be document `_id`s from the `Story` model. We also added
+a `_creator` `ObjectId` to our `Story` schema which refers to a single `Person`.
+
+## Saving a ref (to the parent)
+
+Below you can see how we "save" a ref in 'story1' back to the _creator. This
+is usually all you need to do.
+
+
+ var aaron = new Person({ name: 'Aaron', age: 100 });
+
+ aaron.save(function (err) {
+ if (err) ...
+
+ var story1 = new Story({
+ title: "A man who cooked Nintendo"
+ , _creator: aaron._id
+ });
+
+ story1.save(function (err) {
+ if (err) ...
+ });
+ })
+
+## Populating the refs (to the parent)
+
+So far we haven't done anything special. We've merely created a `Person` and
+a `Story`. Now let's take a look at populating our story's `_creator`:
+
+ Story
+ .findOne({ title: /Nintendo/i })
+ .populate('_creator') // <--
+ .run(function (err, story) {
+ if (err) ..
+ console.log('The creator is %s', story._creator.name);
+ // prints "The creator is Aaron"
+ })
+
+Yup that's it. We've just queried for a `Story` with the term Nintendo in it's
+title and also queried the `Person` collection for the story's creator. Nice!
+
+Arrays of `ObjectId` refs work the same way. Just call the `populate` method on the query and
+an array of documents will be returned in place of the `ObjectId`s.
+
+What if we only want a few specific fields returned for the query? This can
+be accomplished by passing an array of field names to the `populate` method:
+
+ Story
+ .findOne({ title: /Nintendo/i })
+ .populate('_creator', ['name']) // <-- only return the Persons name
+ .run(function (err, story) {
+ if (err) ..
+
+ console.log('The creator is %s', story._creator.name);
+ // prints "The creator is Aaron"
+
+ console.log('The creators age is %s', story._creator.age)
+ // prints "The creators age is null'
+ })
+
+Now this is much better. The only property of the creator we are using
+is the `name` so we only returned that field from the db. Efficiency FTW!
+
+
+## References to the children
+
+You may find however, if you use the `aaron` object, you are unable to get
+a list of the `stories`. This is because no `story` objects were ever 'pushed'
+on to `aaron.stories`.
+
+There are two perspectives to this story. First, it's nice to have aaron know
+which are his stories.
+
+ aaron.stories.push(story1);
+ aaron.save();
+
+This allows you do a find & populate like:
+
+ Person
+ .findOne({ name: 'Aaron' })
+ .populate('stories') // <-- only works if you pushed refs to children
+ .run(function (err, person) {
+ if (err) ..
+
+ console.log('JSON for person is: ', person);
+
+ })
+
+However, it is debatable that you really want two sets of pointers as they
+may get out of sync. So you could instead merely find() the documents you
+are interested in.
+
+ Story
+ .find({ _creator: aaron._id })
+ .populate('_creator') // <-- not really necessary
+ .run(function (err, stories) {
+ if (err) ..
+
+ console.log('The stories JSON is an array: ', stoires);
+ })
+
+
+
+
+## Updating
+
+Now that we have a story we realized that the `_creator` was incorrect. We can
+update `ObjectId` refs the same as any other property through the magic of Mongooses
+internal casting:
+
+ var guille = new Person({ name: 'Guillermo' });
+ guille.save(function (err) {
+ if (err) ..
+
+ story._creator = guille; // or guille._id
+
+ story.save(function (err) {
+ if (err) ..
+
+ Story
+ .findOne({ title: /Nintendo/i })
+ .populate('_creator', ['name'])
+ .run(function (err, story) {
+ if (err) ..
+
+ console.log('The creator is %s', story._creator.name)
+ // prints "The creator is Guillermo"
+ })
+
+ })
+ })
+
+### Note:
+
+The documents returned from calling `populate` become fully functional,
+`remove`able, `save`able documents. Do not confuse them with embedded
+docs. Take caution when calling its `remove` method because
+you'll be removing it from the database, not just the array.
diff --git a/node_modules/mongoose/docs/query.md b/node_modules/mongoose/docs/query.md
new file mode 100644
index 0000000..d12892e
--- /dev/null
+++ b/node_modules/mongoose/docs/query.md
@@ -0,0 +1,362 @@
+
+Queries
+=================
+
+A `Query` is what is returned when calling many `Model`
+[methods](/docs/finding-documents.html). These `Query`
+objects provide a chaining api to specify search terms,
+cursor options, hints, and other behavior.
+
+## Query#where
+
+Lets you specify query terms with sugar.
+
+ query.where(path [, val])
+
+`path` is a valid document path. `val` is optional. Its useful to omit
+`val` when used in conjunction with other query methods. For example:
+
+ query
+ .where('name', 'Space Ghost')
+ .where('age').gte(21).lte(65)
+ .run(callback)
+
+In this case, `gte()` and `lte()` operate on the previous path if not
+explicitly passed. The above query results in the following query expression:
+
+ { name: 'Space Ghost', age: { $gte: 21, $lte: 65 }}
+
+## Query#$where
+
+Specifies the javascript function to run on MongoDB on each document scanned.
+See the [MongoDB docs](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-JavascriptExpressionsand%7B%7B%24where%7D%7D) for details.
+
+ Model.find().$where(fn)
+
+`fn` can be either a function or a string.
+
+## Query#$or, Query#or
+
+Specifies the [$or](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24or) operator.
+
+ query.$or(array);
+
+`array` is an array of expressions.
+
+ query.or([{ color: 'blue' }, { color: 'red' }]);
+
+## Query#gt, Query#$gt
+
+Specifies a [greater than](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%3C%2C%3C%3D%2C%3E%2C%3E%3D) expression.
+
+ query.$gt(path, val);
+
+`$gt` is also one of the methods with extra chaining sugar: when only one
+argument is passed, it uses the path used the last call to `where()`. Example:
+
+ Model.where('age').$gt(21)
+
+Results in:
+
+ { age: { $gt: 21 }}
+
+## Query#gte, Query#$gte
+
+Specifies a [greater than or equal to](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%3C%2C%3C%3D%2C%3E%2C%3E%3D) expression.
+
+ query.$gte(path, val);
+
+`$gte` is also one of the methods with extra chaining sugar: when only one
+argument is passed, it uses the path used the last call to `where()`. Example:
+
+ Model.where('age').$gte(21)
+
+Results in:
+
+ { age: { $gte: 21 }}
+
+## Query#lt,Query#$lt
+
+Specifies a [less than](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%3C%2C%3C%3D%2C%3E%2C%3E%3D) expression.
+
+ query.$lt(path, val);
+
+`$lt` is also one of the methods with extra chaining sugar: when only one
+argument is passed, it uses the path used the last call to `where()`. Example:
+
+ Model.where('age').$lt(21)
+
+Results in:
+
+ { age: { $lt: 21 }}
+
+## Query#lte, Query#$lte
+
+Specifies a [less than or equal to](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%3C%2C%3C%3D%2C%3E%2C%3E%3D) expression.
+
+ query.$lte(path, val);
+
+`$lte` is also one of the methods with extra chaining sugar: when only one
+argument is passed, it uses the path used the last call to `where()`. Example:
+
+ Model.where('age').$lte(21)
+
+Results in:
+
+ { age: { $lte: 21 }}
+
+## Query#ne, Query#$ne, Query#notEqualTo
+
+Specifies the [$ne](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24ne) operator.
+
+ query.$ne(path, val);
+
+These methods have extra sugar in that
+when only one argument is passed, the path in the last call
+to `where()` is used. Example:
+
+ query.where('_id').ne('4ecf92f31993a52c58e07f6a')
+
+and
+
+ query.notEqualTo('_id', '4ecf92f31993a52c58e07f6a')
+
+both result in
+
+ { _id: { $ne: ObjectId('4ecf92f31993a52c58e07f6a') }}
+
+## Query#in, Query#$in
+
+Specifies the [$in](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24in) operator.
+
+ query.$in(path, array)
+
+These methods have extra sugar in that
+when only one argument is passed, the path in the last call
+to `where()` is used.
+
+ query.where('tags').in(['game', 'fun', 'holiday'])
+
+results in
+
+ { tags: { $in: ['game', 'fun', 'holiday'] }}
+
+## Query#nin, Query#$nin
+
+Specifies the [$nin](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24nin) operator.
+
+ query.$nin(path, array)
+
+These methods have extra sugar in that
+when only one argument is passed, the path in the last call
+to `where()` is used.
+
+ query.where('games').nin(['boring', 'lame'])
+
+results in
+
+ { games: { $nin: ['boring', 'lame'] }}
+
+## Query#all, Query#$all
+
+Specifies the [$all](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24all) operator.
+
+ query.$all(path, array)
+
+These methods have extra sugar in that
+when only one argument is passed, the path in the last call
+to `where()` is used.
+
+ query.where('games').all(['fun', 'exhausting'])
+
+results in
+
+ { games: { $all: ['fun', 'exhausting'] }}
+
+## Query#regex, Query#$regex
+
+Specifies the [$regex](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-RegularExpressions) operator.
+
+ query.regex('name.first', /^a/i)
+
+These methods have extra sugar in that
+when only one argument is passed, the path in the last call
+to `where()` is used.
+
+ query.where('name.first').$regex(/^a/i)
+
+results in
+
+ { 'name.first': { $regex: /^a/i }}
+
+## Query#size, Query#$size
+
+Specifies the [$size](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24size) operator.
+
+ query.$size(path, integer)
+
+These methods have extra sugar in that
+when only one argument is passed, the path in the last call
+to `where()` is used.
+
+ query.size('comments', 2)
+ query.where('comments').size(2)
+
+both result in
+
+ { comments: { $size: 2 }}
+
+## Query#mod, Query#$mod
+
+Specifies the [$mod](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24mod) operator.
+
+ var array = [10, 1]
+ query.mod(path, array)
+
+ query.mod(path, 10, 1)
+
+ query.where(path).$mod(10, 1)
+
+all result in
+
+ { path: { $mod: [10, 1] }}
+
+## Query#exists, Query#$exists
+
+Specifies the [$exists](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24exists) operator.
+
+ query.exists(path, Boolean)
+
+These methods have extra sugar in that
+when only one argument is passed, the path from the last call
+to `where()` is used.
+
+Given the following query,
+
+ var query = Model.find();
+
+the following queries
+
+ query.exists('occupation');
+ query.exists('occupation', true);
+ query.where('occupation').exists();
+
+all result in
+
+ { occupation: { $exists: true }}
+
+Another example:
+
+ query.exists('occupation', false)
+ query.where('occupation').exists(false);
+
+result in
+
+ { occupation: { $exists: false }}
+
+## Query#elemMatch, Query#$elemMatch
+
+Specifies the [$elemMatch](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24elemMatch) operator.
+
+ query.elemMatch(path, criteria)
+ query.where(path).elemMatch(criteria)
+
+Functions can also be passed so you can further use query sugar
+to declare the expression:
+
+ query.where(path).elemMatch(function)
+ query.elemMatch(path, function)
+
+In this case a `query` is passed as the only argument into the function.
+
+ query.where('comments').elemMatch(function (elem) {
+ elem.where('author', 'bnoguchi')
+ elem.where('votes').gte(5);
+ });
+
+Results in
+
+ { comments: { $elemMatch: { author: 'bnoguchi', votes: { $gte: 5 }}}}
+
+## Query#within, Query#$within
+
+## Query#box, Query#$box
+
+## Query#center, Query#$center
+
+## Query#centerSphere, Query#$centerSphere
+
+## Query#near, Query#$near
+
+Specifies the [$near](http://www.mongodb.org/display/DOCS/Geospatial+Indexing#GeospatialIndexing-Querying) operator.
+
+ var array = [10, 1]
+ query.near(path, array)
+
+ query.near(path, 10, 1)
+
+ query.where(path).$near(10, 1)
+
+all result in
+
+ { path: { $near: [10, 1] }}
+
+## Query#maxDistance, Query#$maxDistance
+
+Specifies the [$maxDistance](http://www.mongodb.org/display/DOCS/Geospatial+Indexing#GeospatialIndexing-Querying) operator.
+
+ query.where('checkin').near([40, -72]).maxDistance(1);
+
+results in
+
+ { checkin: { $near: [40, -72], $maxDistance: 1 }}
+
+## Query#select
+## Query#fields
+
+## Query#only
+## Query#exclude
+
+## Query#slice
+## Query#$slice
+
+## Query#populate
+
+// sorting
+## Query#sort
+## Query#asc
+## Query#desc
+
+// options
+## Query#limit
+## Query#skip
+## Query#maxscan
+## Query#snapshot
+## Query#batchSize
+## Query#slaveOk
+## Query#hint
+
+// executing
+## Query#find
+
+ query.find(criteria [, callback])
+
+## Query#findOne
+## Query#count
+## Query#distinct
+## Query#update
+## Query#remove
+
+## Query#run
+## Query#exec
+
+ query.run([callback])
+
+## Query#each
+
+## Query#stream
+
+Returns a [QueryStream](/docs/querystream.html) for the query.
+
+ Model.find({}).stream().pipe(writeStream)
+
diff --git a/node_modules/mongoose/docs/querystream.md b/node_modules/mongoose/docs/querystream.md
new file mode 100644
index 0000000..a2a7300
--- /dev/null
+++ b/node_modules/mongoose/docs/querystream.md
@@ -0,0 +1,6 @@
+
+Querystreams
+=================
+
+Docs coming soon
+
diff --git a/node_modules/mongoose/docs/schematypes.md b/node_modules/mongoose/docs/schematypes.md
new file mode 100644
index 0000000..f1e2787
--- /dev/null
+++ b/node_modules/mongoose/docs/schematypes.md
@@ -0,0 +1,197 @@
+
+Schema Types
+============
+
+`SchemaType`s take care of validation, casting, defaults, and other general
+options in our models. We can specify our types one of two ways:
+
+ // directly without options
+ var Person = new Schema({
+ title : String
+ });
+
+ // or with options
+ var Person = new Schema({
+ title : { type: String, lowercase: true }
+ });
+
+In the example above we specified the `lowercase` option for strings which
+will lowercase the string whenever it is set. Options are functions that are
+called on each SchemaType. Each `SchemaType` has its own set of custom options.
+
+## Available Schema Types
+
+### String
+
+ - `lowercase`: {Boolean}
+
+ Creates a setter which calls `.toLowerCase()` on the value
+
+ - `uppercase`: {Boolean}
+
+ Creates a setter which calls `.toUpperCase()` on the value
+
+ - `trim`: {Boolean}
+
+ Creates a setter which calls `.trim()` on the value
+
+ - `match`: {RegExp}
+
+ Creates a RegExp based [validator](/docs/validation.html). The value being set is `.test()`ed
+ against the RegExp. If it does not pass, validation will fail.
+
+ - `enum`: {Array}
+
+ Creates an enum validator. If the value being set is not in this
+ array, validation will fail.
+
+### Number
+
+ - `min`: {Number}
+
+ Creates a validator which checks that the value being set is not less
+ than the value specified.
+
+ - `max`: {Number}
+
+ Creates a validator which checks that the value being set is not greater
+ than the value specified.
+
+### Date
+
+ - no custom options
+
+### Boolean
+
+ - no custom options
+
+### Buffer (v2.x only)
+
+ - no custom options
+
+### ObjectId
+
+ To specify a type of `ObjectId`, use `Schema.ObjectId` in your declaration.
+
+ var mongoose = require('mongoose');
+ var Schema = mongoose.Schema;
+ var Car = new Schema({ driver: Schema.ObjectId })
+
+ - no custom options
+
+### Mixed
+
+ An "anything goes" `SchemaType`, its flexibility comes at a trade-off of it being
+ harder to maintain. `Mixed` is available either through `Schema.Types.Mixed` or
+ by passing an empty object literal. The following are equivalent:
+
+ var Any = new Schema({ any: {} });
+ var Any = new Schema({ any: Schema.Types.Mixed });
+
+ Since it is a schema-less type, you can change the value to anything else
+ you like, but Mongoose loses the ability to auto detect/save those changes.
+ To "tell" Mongoose that the value of a `Mixed` type has changed, call
+ the `.markModified(path)` method of the document passing the path to
+ the `Mixed` type you just changed.
+
+ person.anything = { x: [3, 4, { y: "changed" }] };
+ person.markModified('anything');
+ person.save(); // anything will now get saved
+
+ - no custom options
+
+### Array
+
+ Creates an array of `SchemaTypes` or [Embedded Documents](/docs/embedded-documents.html).
+
+ var ToySchema = new Schema({ name: String });
+ var ToyBox = new Schema({
+ toys: [ToySchema]
+ , buffers: [Buffer]
+ , string: [String]
+ , numbers: [Number]
+ ... etc
+ });
+
+ Note: specifying an empty array is equivalent to `[Mixed]`. The following all
+ create arrays of `Mixed`:
+
+ var Empty1 = new Schema({ any: [] });
+ var Empty2 = new Schema({ any: Array });
+ var Empty3 = new Schema({ any: [Schema.Types.Mixed] });
+ var Empty4 = new Schema({ any: [{}] });
+
+ - no custom options
+
+## Additional options
+
+Besides the options listed above, all SchemaTypes share the following additional
+options.
+
+ - `default`: {Function|value} - Determines the default value for the path. All values are casted. If using a function, the value it returns will be casted as the default value.
+
+ - `required`: {Boolean} - If true, creates a validation rule requiring this path be set before saving occurs.
+
+ - `get`: {Function} - Adds a getter for this path. See the [getters / setters](/docs/getters-setters.html) docs for more detail.
+
+ - `set`: {Function} - Adds a setter for this path. See the [getters / setters](/docs/getters-setters.html) docs for more detail.
+
+ - `index`: {Boolean|Object} - Tells Mongoose to ensure an index is created for this path. An object can be passed as well.
+
+ var Person = new Schema({ name: String, index: true })
+ var Person = new Schema({ name: String, index: { unique: true }})
+
+ Note: indexes cannot be created for `Buffer` `SchemaTypes`. <br>
+ Note: if the index already exists on the db, it will _not_ be replaced.
+
+ - `unique`: {Boolean} - Tells Mongoose to ensure a unique index is created for this path. The following are equivalent:
+
+ var Person = new Schema({ name: String, unique: true })
+ var Person = new Schema({ name: String, index: { unique: true }})
+
+ Note: indexes cannot be created for `Buffer` `SchemaTypes`. <br>
+ Note: if the index already exists on the db, it will _not_ be replaced.
+
+ - `sparse`: {Boolean} - Tells Mongoose to ensure a sparse index is created for this path. The following are equivalent:
+
+ var Person = new Schema({ name: String, sparse: true })
+ var Person = new Schema({ name: String, index: { sparse: true }})
+
+ Note: indexes cannot be created for `Buffer` `SchemaTypes`. <br>
+ Note: if the index already exists on the db, it will _not_ be replaced.
+
+ - `validate`: {Function|RegExp|Array} - Creates a [validator](/docs/validation.html) for this path.
+
+ // passing a function
+ function hasNumber (v) {
+ return v.length && /\d/.test(v);
+ }
+ var Person = new Schema({ street: String, validate: hasNumber });
+
+ // passing a RegExp
+ var Person = new Schema({ street: String, validate: /\d/ });
+
+ // passing an array
+ var Person = new Schema({ street: String, validate: [hasNumber, 'street number required'] });
+
+ // or
+ var Person = new Schema({ street: String, validate: [/\d/, 'street number required'] });
+
+ For more detail about validation including async validation, see the [validation](/docs/validation.html) page.
+
+## Alternate options definition
+
+Instead of defining options when instanciating your `Schema` we can also
+access keys through the `path` function and add options there:
+
+ Person.path('age').max(400);
+
+ Person.path('meta.birth').set(function (v) {
+ // this is a setter
+ });
+
+ Person.path('title').validate(function (v) {
+ return v.length > 50;
+ });
+
+
diff --git a/node_modules/mongoose/docs/validation.md b/node_modules/mongoose/docs/validation.md
new file mode 100644
index 0000000..7748e8b
--- /dev/null
+++ b/node_modules/mongoose/docs/validation.md
@@ -0,0 +1,92 @@
+
+Validation in models
+====================
+
+Before we get into the specifics of validation syntax, please keep the
+following rules in mind:
+
+- Validation is defined in the `Schema`
+
+- Validation occurs when a document attempts to be saved, after defaults have
+been applied.
+
+- Mongoose doesn't care about complex error message construction. Errors have
+type identifiers. For example, `"min"` is the identifier for the error
+triggered when a number doesn't meet the minimum value. The path and value
+that triggered the error can be accessed in the `ValidationError` object.
+
+- Validation is an internal piece of middleware
+
+- Validation is asynchronously recursive: when you call `Model#save`, embedded
+ documents validation is executed. If an error happens, your `Model#save`
+ callback receives it.
+
+## Simple validation
+
+Simple validation is declared by passing a function to `validate` and an error
+type to your `SchemaType` \(please read the chapter on [model definition](/docs/model-definition.html) to learn
+more about schemas).
+
+ function validator (v) {
+ return v.length > 5;
+ };
+
+ new Schema({
+ name: { type: String, validate: [validator, 'my error type'] }
+ })
+
+If you find this syntax too clumsy, you can also define the type
+
+ var schema = new Schema({
+ name: String
+ })
+
+and then your validator
+
+ schema.path('name').validate(function (v) {
+ return v.length > 5;
+ }, 'my error type');
+
+## Regular expressions
+
+If you want to test a certain value against a regular expression:
+
+ var schema = new Schema({
+ name: { type: String, validate: /[a-z]/ }
+ });
+
+## Asynchronous validation
+
+If you define a validator function with two parameters, like:
+
+ schema.path('name').validate(function (v, fn) {
+ // my logic
+ }, 'my error type');
+
+Then the function `fn` has to be called with `true` or `false`, depending on
+whether the validator passed. This allows for calling other models and querying
+data asynchronously from your validator.
+
+## Built in validators
+
+Strings:
+
+ - `enum`: takes a list of allowed values. Example:
+
+ var Post = new Schema({
+ type: { type: String, enum: ['page', 'post', 'link'] }
+ })
+
+Numbers:
+
+ - `min`: minimum value
+
+ var Person = new Schema({
+ age: { type: Number, min: 5 }
+ })
+
+ - `max`: maxmimum value
+
+ var Person = new Schema({
+ age: { type: Number, max: 100 }
+ })
diff --git a/node_modules/mongoose/docs/virtuals.md b/node_modules/mongoose/docs/virtuals.md
new file mode 100644
index 0000000..65bc2bf
--- /dev/null
+++ b/node_modules/mongoose/docs/virtuals.md
@@ -0,0 +1,84 @@
+Virtual attributes
+====================
+
+Mongoose supports virtual attributes. Virtual attributes are attributes
+that are convenient to have around but that do not get persisted to mongodb.
+
+An example is helpful.
+
+## Example
+Take the following schema:
+
+ var PersonSchema = new Schema({
+ name: {
+ first: String
+ , last: String
+ }
+ });
+
+ var Person = mongoose.model('Person', PersonSchema);
+
+ var theSituation = new Person({name: { first: 'Michael', last: 'Sorrentino' }});
+
+Suppose you want to write `theSituation`'s full name. You could do so via:
+
+ console.log(theSituation.name.first + ' ' + theSituation.name.last);
+
+It is more convenient to define a virtual attribute, `name.full`, so you can instead write:
+
+ console.log(theSituation.name.full);
+
+To do so, you can declare a virtual attribute on the Schema, `Person`:
+
+ PersonSchema
+ .virtual('name.full')
+ .get(function () {
+ return this.name.first + ' ' + this.name.last;
+ });
+
+So when you get `name.full`, via
+
+ theSituation.name.full;
+
+the implementation ends up invoking the getter function
+
+ function () {
+ return this.name.first + ' ' + this.name.last;
+ }
+
+and returning it.
+
+It would also be nice to be able to set `this.name.first` and `this.name.last` by setting `this.name.full`. For example, it would be nice if we wanted to change theSituation's `name.first` and `name.last` to 'The' and 'Situation' respectively just by invoking:
+
+ theSituation.set('name.full', 'The Situation');
+
+Mongoose allows you to do this, too, via virtual attribute setters. You can define a virtual attribute setter thusly:
+
+ PersonSchema
+ .virtual('name.full')
+ .get(function () {
+ return this.name.first + ' ' + this.name.last;
+ })
+ .set(function (setFullNameTo) {
+ var split = setFullNameTo.split(' ')
+ , firstName = split[0]
+ , lastName = split[1];
+
+ this.set('name.first', firstName);
+ this.set('name.last', lastName);
+ });
+
+Then, when you invoke:
+
+ theSituation.set('name.full', 'The Situation');
+
+and you save the document, then `name.first` and `name.last` will be changed in monbodb, but the mongodb document will not have persisted a `name.full` key or value to the database:
+
+ theSituation.save(function (err) {
+ Person.findById(theSituation._id, function (err, found) {
+ console.log(found.name.first); // 'The'
+ console.log(found.name.last); // 'Situation'
+ });
+ });
+
+If you want attributes that you can get and set but that are not themselves persisted to mongodb, virtual attributes is the Mongoose feature for you.
diff --git a/node_modules/mongoose/examples/schema.js b/node_modules/mongoose/examples/schema.js
new file mode 100644
index 0000000..d108d05
--- /dev/null
+++ b/node_modules/mongoose/examples/schema.js
@@ -0,0 +1,102 @@
+
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('mongoose')
+ , Schema = mongoose.Schema;
+
+/**
+ * Schema definition
+ */
+
+// recursive embedded-document schema
+
+var Comment = new Schema();
+
+Comment.add({
+ title : { type: String, index: true }
+ , date : Date
+ , body : String
+ , comments : [Comment]
+});
+
+var BlogPost = new Schema({
+ title : { type: String, index: true }
+ , slug : { type: String, lowercase: true, trim: true }
+ , date : Date
+ , buf : Buffer
+ , comments : [Comment]
+ , creator : Schema.ObjectId
+});
+
+var Person = new Schema({
+ name: {
+ first: String
+ , last : String
+ }
+ , email: { type: String, required: true, index: { unique: true, sparse: true } }
+ , alive: Boolean
+});
+
+/**
+ * Accessing a specific schema type by key
+ */
+
+BlogPost.path('date')
+.default(function(){
+ return new Date()
+ })
+.set(function(v){
+ return v == 'now' ? new Date() : v;
+ });
+
+/**
+ * Pre hook.
+ */
+
+BlogPost.pre('save', function(next, done){
+ emailAuthor(done); // some async function
+ next();
+});
+
+/**
+ * Methods
+ */
+
+BlogPost.methods.findCreator = function (callback) {
+ return this.db.model('Person').findById(this.creator, callback);
+}
+
+BlogPost.statics.findByTitle = function (title, callback) {
+ return this.find({ title: title }, callback);
+}
+
+BlogPost.methods.expressiveQuery = function (creator, date, callback) {
+ return this.find('creator', creator).where('date').gte(date).run(callback);
+}
+
+/**
+ * Plugins
+ */
+
+function slugGenerator (options){
+ options = options || {};
+ var key = options.key || 'title';
+
+ return function slugGenerator(schema){
+ schema.path(key).set(function(v){
+ this.slug = v.toLowerCase().replace(/[^a-z0-9]/g, '').replace(/-+/g, '');
+ return v;
+ });
+ };
+};
+
+BlogPost.plugin(slugGenerator());
+
+/**
+ * Define model.
+ */
+
+mongoose.model('BlogPost', BlogPost);
+mongoose.model('Person', Person);
diff --git a/node_modules/mongoose/index.js b/node_modules/mongoose/index.js
new file mode 100644
index 0000000..e7e6278
--- /dev/null
+++ b/node_modules/mongoose/index.js
@@ -0,0 +1,7 @@
+
+/**
+ * Export lib/mongoose
+ *
+ */
+
+module.exports = require('./lib/');
diff --git a/node_modules/mongoose/lib/collection.js b/node_modules/mongoose/lib/collection.js
new file mode 100644
index 0000000..c4d5e79
--- /dev/null
+++ b/node_modules/mongoose/lib/collection.js
@@ -0,0 +1,167 @@
+
+/**
+ * Collection constructor
+ *
+ * @param {String} collection name
+ * @param {Collection} connection object
+ * @api public
+ */
+
+function Collection (name, conn) {
+ this.name = name;
+ this.conn = conn;
+ this.buffer = true;
+ this.queue = [];
+ if (this.conn.readyState == 1) this.onOpen();
+};
+
+/**
+ * The collection name
+ *
+ * @api public
+ */
+
+Collection.prototype.name;
+
+/**
+ * The Connection instance
+ *
+ * @api public
+ */
+
+Collection.prototype.conn;
+
+/**
+ * Called when the database connects
+ *
+ * @api private
+ */
+
+Collection.prototype.onOpen = function () {
+ var self = this;
+ this.buffer = false;
+ self.doQueue();
+};
+
+/**
+ * Called when the database disconnects
+ *
+ * @api private
+ */
+
+Collection.prototype.onClose = function () {
+ this.buffer = true;
+};
+
+/**
+ * Adds a callback to the queue
+ *
+ * @param {String} method name
+ * @param {Array} arguments
+ * @api private
+ */
+
+Collection.prototype.addQueue = function (name, args) {
+ this.queue.push([name, args]);
+ return this;
+};
+
+/**
+ * Executes the current queue
+ *
+ * @api private
+ */
+
+Collection.prototype.doQueue = function () {
+ for (var i = 0, l = this.queue.length; i < l; i++){
+ this[this.queue[i][0]].apply(this, this.queue[i][1]);
+ }
+ this.queue = [];
+ return this;
+};
+
+/**
+ * Ensure index function
+ *
+ * @api private
+ */
+
+Collection.prototype.ensureIndex = function(){
+ throw new Error('Collection#ensureIndex unimplemented by driver');
+};
+
+/**
+ * FindAndModify command
+ *
+ * @api private
+ */
+
+Collection.prototype.findAndModify = function(){
+ throw new Error('Collection#findAndModify unimplemented by driver');
+};
+
+/**
+ * FindOne command
+ *
+ * @api private
+ */
+
+Collection.prototype.findOne = function(){
+ throw new Error('Collection#findOne unimplemented by driver');
+};
+
+/**
+ * Find command
+ *
+ * @api private
+ */
+
+Collection.prototype.find = function(){
+ throw new Error('Collection#find unimplemented by driver');
+};
+
+/**
+ * Insert command
+ *
+ * @api private
+ */
+
+Collection.prototype.insert = function(){
+ throw new Error('Collection#insert unimplemented by driver');
+};
+
+/**
+ * Update command
+ *
+ * @api private
+ */
+
+Collection.prototype.save = function(){
+ throw new Error('Collection#save unimplemented by driver');
+};
+
+/**
+ * Insert command
+ *
+ * @api private
+ */
+
+Collection.prototype.update = function(){
+ throw new Error('Collection#update unimplemented by driver');
+};
+
+/**
+ * getIndexes command
+ *
+ * @api private
+ */
+
+Collection.prototype.getIndexes = function(){
+ throw new Error('Collection#getIndexes unimplemented by driver');
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = Collection;
diff --git a/node_modules/mongoose/lib/connection.js b/node_modules/mongoose/lib/connection.js
new file mode 100644
index 0000000..37f7e2a
--- /dev/null
+++ b/node_modules/mongoose/lib/connection.js
@@ -0,0 +1,517 @@
+/**
+ * Module dependencies.
+ */
+
+var url = require('url')
+ , utils = require('./utils')
+ , EventEmitter = utils.EventEmitter
+ , driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native'
+ , Model = require('./model')
+ , Schema = require('./schema')
+ , Collection = require(driver + '/collection');
+
+/**
+ * Connection constructor. For practical reasons, a Connection equals a Db
+ *
+ * @param {Mongoose} mongoose base
+ * @api public
+ */
+
+function Connection (base) {
+ this.base = base;
+ this.collections = {};
+ this.models = {};
+};
+
+/**
+ * Inherit from EventEmitter.
+ *
+ */
+
+Connection.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Connection ready state:
+ * 0 = Disconnected
+ * 1 = Connected
+ * 2 = Connecting
+ * 3 = Disconnecting
+ *
+ * @api public
+ */
+
+Connection.prototype.readyState = 0;
+
+/**
+ * A hash of the collections associated with this connection
+ */
+
+Connection.prototype.collections;
+
+/**
+ * The mongodb.Db instance, set when the connection is opened
+ *
+ * @api public
+ */
+
+Connection.prototype.db;
+
+/**
+ * Establishes the connection
+ *
+ * `options` is a hash with the following optional properties:
+ *
+ * options.db - passed to the connection db instance
+ * options.server - passed to the connection server instance(s)
+ * options.replset - passed to the connection ReplSetServer instance
+ * options.user - username for authentication
+ * options.pass - password for authentication
+ *
+ * Notes:
+ *
+ * Mongoose forces the db option `forceServerObjectId` false and cannot
+ * be overridden.
+ *
+ * Mongoose defaults the server `auto_reconnect` options to true which
+ * can be overridden.
+ *
+ * See the node-mongodb-native driver instance for options that it
+ * understands.
+ *
+ * @param {String} mongodb://uri
+ * @return {Connection} self
+ * @see https://github.com/christkv/node-mongodb-native
+ * @api public
+ */
+
+Connection.prototype.open = function (host, database, port, options, callback) {
+ var self = this
+ , uri;
+
+ if ('string' === typeof database) {
+ switch (arguments.length) {
+ case 2:
+ port = 27017;
+ case 3:
+ switch (typeof port) {
+ case 'function':
+ callback = port, port = 27017;
+ break;
+ case 'object':
+ options = port, port = 27017;
+ break;
+ }
+ break;
+ case 4:
+ if ('function' === typeof options)
+ callback = options, options = {};
+ }
+ } else {
+ switch (typeof database) {
+ case 'function':
+ callback = database, database = undefined;
+ break;
+ case 'object':
+ options = database;
+ database = undefined;
+ callback = port;
+ break;
+ }
+
+ uri = url.parse(host);
+ host = uri.hostname;
+ port = uri.port || 27017;
+ database = uri.pathname && uri.pathname.replace(/\//g, '');
+ }
+
+ callback = callback || noop;
+ this.options = this.defaultOptions(options);
+
+ // make sure we can open
+ if (0 !== this.readyState) {
+ var err = new Error('Trying to open unclosed connection.');
+ err.state = this.readyState;
+ callback(err);
+ return this;
+ }
+
+ if (!host) {
+ callback(new Error('Missing connection hostname.'));
+ return this;
+ }
+
+ if (!database) {
+ callback(new Error('Missing connection database.'));
+ return this;
+ }
+
+ // handle authentication
+ if (uri && uri.auth) {
+ var auth = uri.auth.split(':');
+ this.user = auth[0];
+ this.pass = auth[1];
+
+ // Check hostname for user/pass
+ } else if (/@/.test(host) && /:/.test(host.split('@')[0])) {
+ host = host.split('@');
+ var auth = host.shift().split(':');
+ host = host.pop();
+ this.user = auth[0];
+ this.pass = auth[1];
+
+ // user/pass options
+ } else if (options && options.user && options.pass) {
+ this.user = options.user;
+ this.pass = options.pass;
+
+ } else {
+ this.user = this.pass = undefined;
+ }
+
+ this.name = database;
+ this.host = host;
+ this.port = port;
+
+ // signal connecting
+ this.readyState = 2;
+ this.emit('opening');
+
+ // open connection
+ this.doOpen(function (err) {
+ if (err) {
+ if (self._events && self._events.error &&
+ ('function' == typeof self._events.error || self._events.error.length)) {
+ self.emit("error", err);
+ }
+ self.readyState = 0;
+ } else {
+ self.onOpen();
+ }
+
+ callback(err || null);
+ });
+
+ return this;
+};
+
+/**
+ * Connects to a replica set.
+ *
+ * Supply a comma-separted list of mongodb:// URIs. You only need to specify
+ * the database name and/or auth to one of them.
+ *
+ * The options parameter is passed to the low level connection. See the
+ * node-mongodb-native driver instance for detail.
+ *
+ * @param {String} comma-separated mongodb:// URIs
+ * @param {String} optional database name
+ * @param {Object} optional options
+ * @param {Function} optional callback
+ */
+
+Connection.prototype.openSet = function (uris, database, options, callback) {
+ var uris = uris.split(',')
+ , self = this;
+
+ switch (arguments.length) {
+ case 3:
+ this.name = database;
+ if ('function' === typeof options) callback = options, options = {};
+ break;
+ case 2:
+ switch (typeof database) {
+ case 'string':
+ this.name = database;
+ case 'function':
+ callback = database, database = null;
+ break;
+ case 'object':
+ options = database, database = null;
+ break;
+ }
+ }
+
+ this.options = options = this.defaultOptions(options);
+ callback = callback || noop;
+
+ if (uris.length < 2) {
+ callback(new Error('Please provide comma-separated URIs'));
+ return this;
+ }
+
+ this.host = [];
+ this.port = [];
+
+ uris.forEach(function (uri) {
+ var uri = url.parse(uri);
+
+ self.host.push(uri.hostname);
+ self.port.push(uri.port || 27017);
+
+ if (!self.name && uri.pathname.replace(/\//g, ''))
+ self.name = uri.pathname.replace(/\//g, '');
+
+ if (!self.user && uri.auth) {
+ var auth = uri.auth.split(':');
+ self.user = auth[0];
+ self.pass = auth[1];
+ }
+ });
+
+ if (!this.name) {
+ callback(new Error('No database name provided for replica set'));
+ return this;
+ }
+
+ this.readyState = 2;
+ this.emit('opening');
+
+ // open connection
+ this.doOpenSet(function (err) {
+ if (err) {
+ if (self._events && self._events.error && self._events.error.length) {
+ self.emit("error", err);
+ }
+ self.readyState = 0;
+ } else {
+ self.onOpen();
+ }
+
+ callback(err || null);
+ });
+};
+
+/**
+ * Called when the connection is opened
+ *
+ * @api private
+ */
+
+Connection.prototype.onOpen = function () {
+ var self = this;
+
+ function open () {
+ self.readyState = 1;
+
+ // avoid having the collection subscribe to our event emitter
+ // to prevent 0.3 warning
+ for (var i in self.collections)
+ self.collections[i].onOpen();
+
+ self.emit('open');
+ };
+
+ // re-authenticate
+ if (self.user && self.pass)
+ self.db.authenticate(self.user, self.pass, open);
+ else
+ open();
+};
+
+/**
+ * Closes the connection
+ *
+ * @param {Function} optional callback
+ * @return {Connection} self
+ * @api public
+ */
+
+Connection.prototype.close = function (callback) {
+ var self = this
+ , callback = callback || function(){};
+
+ switch (this.readyState){
+ case 0: // disconnected
+ callback(null);
+ break;
+
+ case 1: // connected
+ this.readyState = 3;
+ this.doClose(function(err){
+ if (err){
+ callback(err);
+ } else {
+ self.onClose();
+ callback(null);
+ }
+ });
+ break;
+
+ case 2: // connecting
+ this.once('open', function(){
+ self.close(callback);
+ });
+ break;
+
+ case 3: // disconnecting
+ this.once('close', function () {
+ callback(null);
+ });
+ break;
+ }
+
+ return this;
+};
+
+/**
+ * Called when the connection closes
+ *
+ * @api private
+ */
+
+Connection.prototype.onClose = function () {
+ this.readyState = 0;
+
+ // avoid having the collection subscribe to our event emitter
+ // to prevent 0.3 warning
+ for (var i in this.collections)
+ this.collections[i].onClose();
+
+ this.emit('close');
+};
+
+/**
+ * Retrieves a collection, creating it if not cached.
+ *
+ * @param {String} collection name
+ * @return {Collection} collection instance
+ * @api public
+ */
+
+Connection.prototype.collection = function (name) {
+ if (!(name in this.collections))
+ this.collections[name] = new Collection(name, this);
+ return this.collections[name];
+};
+
+/**
+ * Defines a model or retrieves it
+ *
+ * @param {String} model name
+ * @param {Schema} schema object
+ * @param {String} collection name (optional, induced from model name)
+ * @api public
+ */
+
+Connection.prototype.model = function (name, schema, collection) {
+ if (!this.models[name]) {
+ var model = this.base.model(name, schema, collection, true)
+ , Model
+
+ if (this != model.prototype.connection) {
+ // subclass model using this connection and collection name
+ Model = function Model () {
+ model.apply(this, arguments);
+ };
+
+ Model.__proto__ = model;
+ Model.prototype.__proto__ = model.prototype;
+ Model.prototype.db = this;
+
+ // collection name discovery
+ if ('string' === typeof schema) {
+ collection = schema;
+ }
+
+ if (!collection) {
+ collection = model.prototype.schema.set('collection') || utils.toCollectionName(name);
+ }
+
+ Model.prototype.collection = this.collection(collection);
+ Model.init();
+ }
+
+ this.models[name] = Model || model;
+ }
+
+ return this.models[name];
+};
+
+/**
+ * Set profiling level.
+ *
+ * @param {Int|String} level - Either off (0), slow (1), or all (2)
+ * @param {Int} [ms] If profiling `level` is set to 1, this determines
+ * the threshold in milliseconds above which queries
+ * will be logged. Defaults to 100. (optional)
+ * @param {Function} callback
+ * @api public
+ */
+
+Connection.prototype.setProfiling = function (level, ms, callback) {
+ if (1 !== this.readyState) {
+ return this.on('open', this.setProfiling.bind(this, level, ms, callback));
+ }
+
+ if (!callback) callback = ms, ms = 100;
+
+ var cmd = {};
+
+ switch (level) {
+ case 0:
+ case 'off':
+ cmd.profile = 0;
+ break;
+ case 1:
+ case 'slow':
+ cmd.profile = 1;
+ if ('number' !== typeof ms) {
+ ms = parseInt(ms, 10);
+ if (isNaN(ms)) ms = 100;
+ }
+ cmd.slowms = ms;
+ break;
+ case 2:
+ case 'all':
+ cmd.profile = 2;
+ break;
+ default:
+ return callback(new Error('Invalid profiling level: '+ level));
+ }
+
+ this.db.executeDbCommand(cmd, function (err, resp) {
+ if (err) return callback(err);
+
+ var doc = resp.documents[0];
+
+ err = 1 === doc.ok
+ ? null
+ : new Error('Could not set profiling level to: '+ level)
+
+ callback(err, doc);
+ });
+};
+
+/**
+ * Prepares default connection options.
+ *
+ * @param {Object} options
+ * @api private
+ */
+
+Connection.prototype.defaultOptions = function (options) {
+ var o = options || {};
+
+ o.server = o.server || {};
+
+ if (!('auto_reconnect' in o.server)) {
+ o.server.auto_reconnect = true;
+ }
+
+ o.db = o.db || {};
+ o.db.forceServerObjectId = false;
+
+ return o;
+}
+
+/**
+ * Noop.
+ */
+
+function noop () {}
+
+/**
+ * Module exports.
+ */
+
+module.exports = Connection;
diff --git a/node_modules/mongoose/lib/document.js b/node_modules/mongoose/lib/document.js
new file mode 100644
index 0000000..0c80765
--- /dev/null
+++ b/node_modules/mongoose/lib/document.js
@@ -0,0 +1,1223 @@
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = require('events').EventEmitter
+ , MongooseError = require('./error')
+ , MixedSchema = require('./schema/mixed')
+ , Schema = require('./schema')
+ , ValidatorError = require('./schematype').ValidatorError
+ , utils = require('./utils')
+ , clone = utils.clone
+ , isMongooseObject = utils.isMongooseObject
+ , inspect = require('util').inspect
+ , StateMachine = require('./statemachine')
+ , ActiveRoster = StateMachine.ctor('require', 'modify', 'init')
+ , deepEqual = utils.deepEqual
+ , hooks = require('hooks')
+ , DocumentArray
+
+/**
+ * Document constructor.
+ *
+ * @param {Object} values to set
+ * @api private
+ */
+
+function Document (obj, fields) {
+ // node <0.4.3 bug
+ if (!this._events) this._events = {};
+ this.setMaxListeners(0);
+
+ this._strictMode = this.schema.options && this.schema.options.strict;
+
+ if ('boolean' === typeof fields) {
+ this._strictMode = fields;
+ fields = undefined;
+ } else {
+ this._selected = fields;
+ }
+
+ this._doc = this.buildDoc(fields);
+ this._activePaths = new ActiveRoster();
+ var self = this;
+ this.schema.requiredPaths.forEach(function (path) {
+ self._activePaths.require(path);
+ });
+
+ this._saveError = null;
+ this._validationError = null;
+ this.isNew = true;
+
+ if (obj) this.set(obj, undefined, true);
+
+ this._registerHooks();
+ this.doQueue();
+
+ this.errors = undefined;
+ this._shardval = undefined;
+};
+
+/**
+ * Inherit from EventEmitter.
+ */
+
+Document.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Base Mongoose instance for the model. Set by the Mongoose instance upon
+ * pre-compilation.
+ *
+ * @api public
+ */
+
+Document.prototype.base;
+
+/**
+ * Document schema as a nested structure.
+ *
+ * @api public
+ */
+
+Document.prototype.schema;
+
+/**
+ * Whether the document is new.
+ *
+ * @api public
+ */
+
+Document.prototype.isNew;
+
+/**
+ * Validation errors.
+ *
+ * @api public
+ */
+
+Document.prototype.errors;
+
+/**
+ * Builds the default doc structure
+ *
+ * @api private
+ */
+
+Document.prototype.buildDoc = function (fields) {
+ var doc = {}
+ , self = this
+ , exclude
+ , keys
+ , key
+ , ki
+
+ // determine if this doc is a result of a query with
+ // excluded fields
+ if (fields && 'Object' === fields.constructor.name) {
+ keys = Object.keys(fields);
+ ki = keys.length;
+
+ while (ki--) {
+ if ('_id' !== keys[ki]) {
+ exclude = 0 === fields[keys[ki]];
+ break;
+ }
+ }
+ }
+
+ var paths = Object.keys(this.schema.paths)
+ , plen = paths.length
+ , ii = 0
+
+ for (; ii < plen; ++ii) {
+ var p = paths[ii]
+ , type = this.schema.paths[p]
+ , path = p.split('.')
+ , len = path.length
+ , last = len-1
+ , doc_ = doc
+ , i = 0
+
+ for (; i < len; ++i) {
+ var piece = path[i]
+ , def
+
+ if (i === last) {
+ if (fields) {
+ if (exclude) {
+ // apply defaults to all non-excluded fields
+ if (p in fields) continue;
+
+ def = type.getDefault(self);
+ if ('undefined' !== typeof def) doc_[piece] = def;
+
+ } else {
+ // do nothing. only the fields specified in
+ // the query will be populated
+ }
+ } else {
+ def = type.getDefault(self);
+ if ('undefined' !== typeof def) doc_[piece] = def;
+ }
+ } else {
+ doc_ = doc_[piece] || (doc_[piece] = {});
+ }
+ }
+ };
+
+ return doc;
+};
+
+/**
+ * Inits (hydrates) the document without setters.
+ *
+ * Called internally after a document is returned
+ * from mongodb.
+ *
+ * @param {Object} document returned by mongo
+ * @param {Function} callback
+ * @api private
+ */
+
+Document.prototype.init = function (doc, fn) {
+ this.isNew = false;
+
+ init(this, doc, this._doc);
+ this._storeShard();
+
+ this.emit('init');
+ if (fn) fn(null);
+ return this;
+};
+
+/**
+ * Init helper.
+ * @param {Object} instance
+ * @param {Object} obj - raw mongodb doc
+ * @param {Object} doc - object we are initializing
+ * @private
+ */
+
+function init (self, obj, doc, prefix) {
+ prefix = prefix || '';
+
+ var keys = Object.keys(obj)
+ , len = keys.length
+ , schema
+ , path
+ , i;
+
+ while (len--) {
+ i = keys[len];
+ path = prefix + i;
+ schema = self.schema.path(path);
+
+ if (!schema && obj[i] && 'Object' === obj[i].constructor.name) {
+ // assume nested object
+ doc[i] = {};
+ init(self, obj[i], doc[i], path + '.');
+ } else {
+ if (obj[i] === null) {
+ doc[i] = null;
+ } else if (obj[i] !== undefined) {
+ if (schema) {
+ self.try(function(){
+ doc[i] = schema.cast(obj[i], self, true);
+ });
+ } else {
+ doc[i] = obj[i];
+ }
+ }
+ // mark as hydrated
+ self._activePaths.init(path);
+ }
+ }
+};
+
+/**
+ * _storeShard
+ *
+ * Stores the current values of the shard keys
+ * for use later in the doc.save() where clause.
+ *
+ * Shard key values do not / are not allowed to change.
+ *
+ * @param {Object} document
+ * @private
+ */
+
+Document.prototype._storeShard = function _storeShard () {
+ var key = this.schema.options.shardkey;
+ if (!(key && 'Object' == key.constructor.name)) return;
+
+ var orig = this._shardval = {}
+ , paths = Object.keys(key)
+ , len = paths.length
+ , val
+
+ for (var i = 0; i < len; ++i) {
+ val = this.getValue(paths[i]);
+ if (isMongooseObject(val)) {
+ orig[paths[i]] = val.toObject({ depopulate: true })
+ } else if (val.valueOf) {
+ orig[paths[i]] = val.valueOf();
+ } else {
+ orig[paths[i]] = val;
+ }
+ }
+}
+
+// Set up middleware support
+for (var k in hooks) {
+ Document.prototype[k] = Document[k] = hooks[k];
+}
+
+/**
+ * Sets a path, or many paths
+ *
+ * Examples:
+ * // path, value
+ * doc.set(path, value)
+ *
+ * // object
+ * doc.set({
+ * path : value
+ * , path2 : {
+ * path : value
+ * }
+ * }
+ *
+ * @param {String|Object} key path, or object
+ * @param {Object} value, or undefined or a prefix if first parameter is an object
+ * @param @optional {Schema|String|...} specify a type if this is an on-the-fly attribute
+ * @api public
+ */
+
+Document.prototype.set = function (path, val, type) {
+ var constructing = true === type
+ , adhoc = type && true !== type
+ , adhocs
+
+ if (adhoc) {
+ adhocs = this._adhocPaths || (this._adhocPaths = {});
+ adhocs[path] = Schema.interpretAsType(path, type);
+ }
+
+ if ('string' !== typeof path) {
+ // new Document({ key: val })
+
+ if (null === path || undefined === path) {
+ var _ = path;
+ path = val;
+ val = _;
+
+ } else {
+ var prefix = val
+ ? val + '.'
+ : '';
+
+ if (path instanceof Document) path = path._doc;
+
+ var keys = Object.keys(path)
+ , i = keys.length
+ , pathtype
+ , key
+
+ while (i--) {
+ key = keys[i];
+ if (null != path[key] && 'Object' === path[key].constructor.name
+ && !(this._path(prefix + key) instanceof MixedSchema)) {
+ this.set(path[key], prefix + key, constructing);
+ } else if (this._strictMode) {
+ pathtype = this.schema.pathType(prefix + key);
+ if ('real' === pathtype || 'virtual' === pathtype) {
+ this.set(prefix + key, path[key], constructing);
+ }
+ } else if (undefined !== path[key]) {
+ this.set(prefix + key, path[key], constructing);
+ }
+ }
+
+ return this;
+ }
+ }
+
+ var schema;
+ if ('virtual' === this.schema.pathType(path)) {
+ schema = this.schema.virtualpath(path);
+ schema.applySetters(val, this);
+ return this;
+ } else {
+ schema = this._path(path);
+ }
+
+ var parts = path.split('.')
+ , obj = this._doc
+ , self = this
+ , pathToMark
+ , subpaths
+ , subpath
+
+ // When using the $set operator the path to the field must already exist.
+ // Else mongodb throws: "LEFT_SUBFIELD only supports Object"
+
+ if (parts.length <= 1) {
+ pathToMark = path;
+ } else {
+ subpaths = parts.map(function (part, i) {
+ return parts.slice(0, i).concat(part).join('.');
+ });
+
+ for (var i = 0, l = subpaths.length; i < l; i++) {
+ subpath = subpaths[i];
+ if (this.isDirectModified(subpath) // earlier prefixes that are already
+ // marked as dirty have precedence
+ || this.get(subpath) === null) {
+ pathToMark = subpath;
+ break;
+ }
+ }
+
+ if (!pathToMark) pathToMark = path;
+ }
+
+ if ((!schema || null === val || undefined === val) ||
+ this.try(function(){
+ // if this doc is being constructed we should not
+ // trigger getters.
+ var cur = constructing ? undefined : self.get(path);
+ var casted = schema.cast(val, self, false, cur);
+ val = schema.applySetters(casted, self);
+ })) {
+
+ if (this.isNew) {
+ this.markModified(pathToMark);
+ } else {
+ var priorVal = this.get(path);
+
+ if (!this.isDirectModified(pathToMark)) {
+ if (undefined === val && !this.isSelected(path)) {
+ // special case:
+ // when a path is not selected in a query its initial
+ // value will be undefined.
+ this.markModified(pathToMark);
+ } else if (!deepEqual(val, priorVal)) {
+ this.markModified(pathToMark);
+ }
+ }
+ }
+
+ for (var i = 0, l = parts.length; i < l; i++) {
+ var next = i + 1
+ , last = next === l;
+
+ if (last) {
+ obj[parts[i]] = val;
+ } else {
+ if (obj[parts[i]] && 'Object' === obj[parts[i]].constructor.name) {
+ obj = obj[parts[i]];
+ } else if (obj[parts[i]] && Array.isArray(obj[parts[i]])) {
+ obj = obj[parts[i]];
+ } else {
+ obj = obj[parts[i]] = {};
+ }
+ }
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Gets a raw value from a path (no getters)
+ *
+ * @param {String} path
+ * @api private
+ */
+
+Document.prototype.getValue = function (path) {
+ var parts = path.split('.')
+ , obj = this._doc
+ , part;
+
+ for (var i = 0, l = parts.length; i < l-1; i++) {
+ part = parts[i];
+ path = convertIfInt(path);
+ obj = obj.getValue
+ ? obj.getValue(part) // If we have an embedded array document member
+ : obj[part];
+ if (!obj) return obj;
+ }
+
+ part = parts[l-1];
+ path = convertIfInt(path);
+
+ return obj.getValue
+ ? obj.getValue(part) // If we have an embedded array document member
+ : obj[part];
+};
+
+function convertIfInt (string) {
+ if (/^\d+$/.test(string)) {
+ return parseInt(string, 10);
+ }
+ return string;
+}
+
+/**
+ * Sets a raw value for a path (no casting, setters, transformations)
+ *
+ * @param {String} path
+ * @param {Object} value
+ * @api private
+ */
+
+Document.prototype.setValue = function (path, val) {
+ var parts = path.split('.')
+ , obj = this._doc;
+
+ for (var i = 0, l = parts.length; i < l-1; i++) {
+ obj = obj[parts[i]];
+ }
+
+ obj[parts[l-1]] = val;
+ return this;
+};
+
+/**
+ * Triggers casting on a specific path
+ *
+ * @todo - deprecate? not used anywhere
+ * @param {String} path
+ * @api public
+ */
+
+Document.prototype.doCast = function (path) {
+ var schema = this.schema.path(path);
+ if (schema)
+ this.setValue(path, this.getValue(path));
+};
+
+/**
+ * Gets a path
+ *
+ * @param {String} key path
+ * @param @optional {Schema|String|...} specify a type if this is an on-the-fly attribute
+ * @api public
+ */
+
+Document.prototype.get = function (path, type) {
+ var adhocs;
+ if (type) {
+ adhocs = this._adhocPaths || (this._adhocPaths = {});
+ adhocs[path] = Schema.interpretAsType(path, type);
+ }
+
+ var schema = this._path(path) || this.schema.virtualpath(path)
+ , pieces = path.split('.')
+ , obj = this._doc;
+
+ for (var i = 0, l = pieces.length; i < l; i++) {
+ obj = null == obj ? null : obj[pieces[i]];
+ }
+
+ if (schema) {
+ obj = schema.applyGetters(obj, this);
+ }
+
+ return obj;
+};
+
+/**
+ * Finds the path in the ad hoc type schema list or
+ * in the schema's list of type schemas
+ * @param {String} path
+ * @api private
+ */
+
+Document.prototype._path = function (path) {
+ var adhocs = this._adhocPaths
+ , adhocType = adhocs && adhocs[path];
+
+ if (adhocType) {
+ return adhocType;
+ } else {
+ return this.schema.path(path);
+ }
+};
+
+/**
+ * Commits a path, marking as modified if needed. Useful for mixed keys
+ *
+ * @api public
+ */
+
+Document.prototype.commit =
+Document.prototype.markModified = function (path) {
+ this._activePaths.modify(path);
+};
+
+/**
+ * Captures an exception that will be bubbled to `save`
+ *
+ * @param {Function} function to execute
+ * @param {Object} scope
+ */
+
+Document.prototype.try = function (fn, scope) {
+ var res;
+ try {
+ fn.call(scope);
+ res = true;
+ } catch (e) {
+ this.error(e);
+ res = false;
+ }
+ return res;
+};
+
+/**
+ * modifiedPaths
+ *
+ * Returns the list of paths that have been modified.
+ *
+ * If we set `documents.0.title` to 'newTitle'
+ * then `documents`, `documents.0`, and `documents.0.title`
+ * are modified.
+ *
+ * @api public
+ * @returns Boolean
+ */
+
+Document.prototype.__defineGetter__('modifiedPaths', function () {
+ var directModifiedPaths = Object.keys(this._activePaths.states.modify);
+
+ return directModifiedPaths.reduce(function (list, path) {
+ var parts = path.split('.');
+ return list.concat(parts.reduce(function (chains, part, i) {
+ return chains.concat(parts.slice(0, i).concat(part).join('.'));
+ }, []));
+ }, []);
+});
+
+/**
+ * Checks if a path or any full path containing path as part of
+ * its path chain has been directly modified.
+ *
+ * e.g., if we set `documents.0.title` to 'newTitle'
+ * then we have directly modified `documents.0.title`
+ * but not directly modified `documents` or `documents.0`.
+ * Nonetheless, we still say `documents` and `documents.0`
+ * are modified. They just are not considered direct modified.
+ * The distinction is important because we need to distinguish
+ * between what has been directly modified and what hasn't so
+ * that we can determine the MINIMUM set of dirty data
+ * that we want to send to MongoDB on a Document save.
+ *
+ * @param {String} path
+ * @returns Boolean
+ * @api public
+ */
+
+Document.prototype.isModified = function (path) {
+ return !!~this.modifiedPaths.indexOf(path);
+};
+
+/**
+ * Checks if a path has been directly set and modified. False if
+ * the path is only part of a larger path that was directly set.
+ *
+ * e.g., if we set `documents.0.title` to 'newTitle'
+ * then we have directly modified `documents.0.title`
+ * but not directly modified `documents` or `documents.0`.
+ * Nonetheless, we still say `documents` and `documents.0`
+ * are modified. They just are not considered direct modified.
+ * The distinction is important because we need to distinguish
+ * between what has been directly modified and what hasn't so
+ * that we can determine the MINIMUM set of dirty data
+ * that we want to send to MongoDB on a Document save.
+ *
+ * @param {String} path
+ * @returns Boolean
+ * @api public
+ */
+
+Document.prototype.isDirectModified = function (path) {
+ return (path in this._activePaths.states.modify);
+};
+
+/**
+ * Checks if a certain path was initialized
+ *
+ * @param {String} path
+ * @returns Boolean
+ * @api public
+ */
+
+Document.prototype.isInit = function (path) {
+ return (path in this._activePaths.states.init);
+};
+
+/**
+ * Checks if a path was selected.
+ * @param {String} path
+ * @return Boolean
+ * @api public
+ */
+
+Document.prototype.isSelected = function isSelected (path) {
+ if (this._selected) {
+
+ if ('_id' === path) {
+ return 0 !== this._selected._id;
+ }
+
+ var paths = Object.keys(this._selected)
+ , i = paths.length
+ , inclusive = false
+ , cur
+
+ while (i--) {
+ cur = paths[i];
+ if ('_id' == cur) continue;
+ inclusive = !! this._selected[cur];
+ break;
+ }
+
+ if (path in this._selected) {
+ return inclusive;
+ }
+
+ i = paths.length;
+
+ while (i--) {
+ cur = paths[i];
+ if ('_id' == cur) continue;
+
+ if (0 === cur.indexOf(path + '.')) {
+ return inclusive;
+ }
+
+ if (0 === (path + '.').indexOf(cur)) {
+ return inclusive;
+ }
+ }
+
+ return ! inclusive;
+ }
+
+ return true;
+}
+
+/**
+ * Validation middleware
+ *
+ * @param {Function} next
+ * @api public
+ */
+
+Document.prototype.validate = function (next) {
+ var total = 0
+ , self = this
+ , validating = {}
+
+ if (!this._activePaths.some('require', 'init', 'modify')) {
+ return complete();
+ }
+
+ function complete () {
+ next(self._validationError);
+ self._validationError = null;
+ }
+
+ this._activePaths.forEach('require', 'init', 'modify', function validatePath (path) {
+ if (validating[path]) return;
+
+ validating[path] = true;
+ total++;
+
+ process.nextTick(function(){
+ var p = self.schema.path(path);
+ if (!p) return --total || complete();
+
+ p.doValidate(self.getValue(path), function (err) {
+ if (err) self.invalidate(path, err);
+ --total || complete();
+ }, self);
+ });
+ });
+
+ return this;
+};
+
+/**
+ * Marks a path as invalid, causing a subsequent validation to fail.
+ *
+ * @param {String} path of the field to invalidate
+ * @param {String/Error} error of the path.
+ * @api public
+ */
+
+Document.prototype.invalidate = function (path, err) {
+ if (!this._validationError) {
+ this._validationError = new ValidationError(this);
+ }
+
+ if (!err || 'string' === typeof err) {
+ err = new ValidatorError(path, err);
+ }
+
+ this._validationError.errors[path] = err;
+}
+
+/**
+ * Resets the atomics and modified states of this document.
+ *
+ * @private
+ * @return {this}
+ */
+
+Document.prototype._reset = function reset () {
+ var self = this;
+ DocumentArray || (DocumentArray = require('./types/documentarray'));
+
+ this._activePaths
+ .map('init', 'modify', function (i) {
+ return self.getValue(i);
+ })
+ .filter(function (val) {
+ return (val && val instanceof DocumentArray && val.length);
+ })
+ .forEach(function (array) {
+ array.forEach(function (doc) {
+ doc._reset();
+ });
+ });
+
+ // clear atomics
+ this._dirty().forEach(function (dirt) {
+ var type = dirt.value;
+ if (type && type._path && type.doAtomics) {
+ type._atomics = {};
+ }
+ });
+
+ // Clear 'modify'('dirty') cache
+ this._activePaths.clear('modify');
+ var self = this;
+ this.schema.requiredPaths.forEach(function (path) {
+ self._activePaths.require(path);
+ });
+
+ return this;
+}
+
+/**
+ * Returns the dirty paths / vals
+ *
+ * @api private
+ */
+
+Document.prototype._dirty = function _dirty () {
+ var self = this;
+
+ var all = this._activePaths.map('modify', function (path) {
+ return { path: path
+ , value: self.getValue(path)
+ , schema: self._path(path) };
+ });
+
+ // Sort dirty paths in a flat hierarchy.
+ all.sort(function (a, b) {
+ return (a.path < b.path ? -1 : (a.path > b.path ? 1 : 0));
+ });
+
+ // Ignore "foo.a" if "foo" is dirty already.
+ var minimal = []
+ , lastReference = null;
+
+ all.forEach(function (item, i) {
+ if (item.path.indexOf(lastReference) !== 0) {
+ lastReference = item.path + '.';
+ minimal.push(item);
+ }
+ });
+
+ return minimal;
+}
+
+/**
+ * Returns if the document has been modified
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+Document.prototype.__defineGetter__('modified', function () {
+ return this._activePaths.some('modify');
+});
+
+/**
+ * Compiles schemas.
+ * @api private
+ */
+
+function compile (tree, proto, prefix) {
+ var keys = Object.keys(tree)
+ , i = keys.length
+ , limb
+ , key;
+
+ while (i--) {
+ key = keys[i];
+ limb = tree[key];
+
+ define(key
+ , (('Object' === limb.constructor.name
+ && Object.keys(limb).length)
+ && (!limb.type || limb.type.type)
+ ? limb
+ : null)
+ , proto
+ , prefix
+ , keys);
+ }
+};
+
+/**
+ * Defines the accessor named prop on the incoming prototype.
+ * @api private
+ */
+
+function define (prop, subprops, prototype, prefix, keys) {
+ var prefix = prefix || ''
+ , path = (prefix ? prefix + '.' : '') + prop;
+
+ if (subprops) {
+
+ Object.defineProperty(prototype, prop, {
+ enumerable: true
+ , get: function () {
+ if (!this.__getters)
+ this.__getters = {};
+
+ if (!this.__getters[path]) {
+ var nested = Object.create(this);
+
+ // save scope for nested getters/setters
+ if (!prefix) nested._scope = this;
+
+ // shadow inherited getters from sub-objects so
+ // thing.nested.nested.nested... doesn't occur (gh-366)
+ var i = 0
+ , len = keys.length;
+
+ for (; i < len; ++i) {
+ // over-write the parents getter without triggering it
+ Object.defineProperty(nested, keys[i], {
+ enumerable: false // It doesn't show up.
+ , writable: true // We can set it later.
+ , configurable: true // We can Object.defineProperty again.
+ , value: undefined // It shadows its parent.
+ });
+ }
+
+ nested.toObject = function () {
+ return this.get(path);
+ };
+
+ compile(subprops, nested, path);
+ this.__getters[path] = nested;
+ }
+
+ return this.__getters[path];
+ }
+ , set: function (v) {
+ return this.set(v, path);
+ }
+ });
+
+ } else {
+
+ Object.defineProperty(prototype, prop, {
+ enumerable: true
+ , get: function ( ) { return this.get.call(this._scope || this, path); }
+ , set: function (v) { return this.set.call(this._scope || this, path, v); }
+ });
+ }
+};
+
+/**
+ * We override the schema setter to compile accessors
+ *
+ * @api private
+ */
+
+Document.prototype.__defineSetter__('schema', function (schema) {
+ compile(schema.tree, this);
+ this._schema = schema;
+});
+
+/**
+ * We override the schema getter to return the internal reference
+ *
+ * @api private
+ */
+
+Document.prototype.__defineGetter__('schema', function () {
+ return this._schema;
+});
+
+/**
+ * Register default hooks
+ *
+ * @api private
+ */
+
+Document.prototype._registerHooks = function _registerHooks () {
+ if (!this.save) return;
+
+ DocumentArray || (DocumentArray = require('./types/documentarray'));
+
+ this.pre('save', function (next) {
+ // we keep the error semaphore to make sure we don't
+ // call `save` unnecessarily (we only need 1 error)
+ var subdocs = 0
+ , error = false
+ , self = this;
+
+ var arrays = this._activePaths
+ .map('init', 'modify', function (i) {
+ return self.getValue(i);
+ })
+ .filter(function (val) {
+ return (val && val instanceof DocumentArray && val.length);
+ });
+
+ if (!arrays.length)
+ return next();
+
+ arrays.forEach(function (array) {
+ subdocs += array.length;
+ array.forEach(function (value) {
+ if (!error)
+ value.save(function (err) {
+ if (!error) {
+ if (err) {
+ error = true;
+ next(err);
+ } else
+ --subdocs || next();
+ }
+ });
+ });
+ });
+ }, function (err) {
+ this.db.emit('error', err);
+ }).pre('save', function checkForExistingErrors (next) {
+ if (this._saveError) {
+ next(this._saveError);
+ this._saveError = null;
+ } else {
+ next();
+ }
+ }).pre('save', function validation (next) {
+ return this.validate(next);
+ });
+};
+
+/**
+ * Registers an error
+ *
+ * @TODO underscore this method
+ * @param {Error} error
+ * @api private
+ */
+
+Document.prototype.error = function (err) {
+ this._saveError = err;
+ return this;
+};
+
+/**
+ * Executes methods queued from the Schema definition
+ *
+ * @TODO underscore this method
+ * @api private
+ */
+
+Document.prototype.doQueue = function () {
+ if (this.schema && this.schema.callQueue)
+ for (var i = 0, l = this.schema.callQueue.length; i < l; i++) {
+ this[this.schema.callQueue[i][0]].apply(this, this.schema.callQueue[i][1]);
+ }
+ return this;
+};
+
+/**
+ * Gets the document
+ *
+ * Available options:
+ *
+ * - getters: apply all getters (path and virtual getters)
+ * - virtuals: apply virtual getters (can override `getters` option)
+ *
+ * Example of only applying path getters:
+ *
+ * doc.toObject({ getters: true, virtuals: false })
+ *
+ * Example of only applying virtual getters:
+ *
+ * doc.toObject({ virtuals: true })
+ *
+ * Example of applying both path and virtual getters:
+ *
+ * doc.toObject({ getters: true })
+ *
+ * @return {Object} plain object
+ * @api public
+ */
+
+Document.prototype.toObject = function (options) {
+ options || (options = {});
+ options.minimize = true;
+
+ var ret = clone(this._doc, options);
+
+ if (options.virtuals || options.getters && false !== options.virtuals) {
+ applyGetters(this, ret, 'virtuals');
+ }
+
+ if (options.getters) {
+ applyGetters(this, ret, 'paths');
+ }
+
+ return ret;
+};
+
+/**
+ * Applies virtuals properties to `json`.
+ *
+ * @param {Document} self
+ * @param {Object} json
+ * @param {String} either `virtuals` or `paths`
+ * @return json
+ * @private
+ */
+
+function applyGetters (self, json, type) {
+ var schema = self.schema
+ , paths = Object.keys(schema[type])
+ , i = paths.length
+ , path
+
+ while (i--) {
+ path = paths[i];
+
+ var parts = path.split('.')
+ , plen = parts.length
+ , last = plen - 1
+ , branch = json
+ , part
+
+ for (var ii = 0; ii < plen; ++ii) {
+ part = parts[ii];
+ if (ii === last) {
+ branch[part] = self.get(path);
+ } else {
+ branch = branch[part] || (branch[part] = {});
+ }
+ }
+ }
+
+ return json;
+}
+
+/**
+ * JSON.stringify helper.
+ *
+ * Implicitly called when a document is passed
+ * to JSON.stringify()
+ *
+ * @return {Object}
+ * @api public
+ */
+
+Document.prototype.toJSON = function (options) {
+ if ('undefined' === typeof options) options = {};
+ options.json = true;
+ return this.toObject(options);
+};
+
+/**
+ * Helper for console.log
+ *
+ * @api public
+ */
+
+Document.prototype.toString =
+Document.prototype.inspect = function (options) {
+ return inspect(this.toObject(options));
+};
+
+/**
+ * Returns true if the Document stores the same data as doc.
+ * @param {Document} doc to compare to
+ * @return {Boolean}
+ * @api public
+ */
+
+Document.prototype.equals = function (doc) {
+ return this.get('_id') === doc.get('_id');
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = Document;
+
+/**
+ * Document Validation Error
+ */
+
+function ValidationError (instance) {
+ MongooseError.call(this, "Validation failed");
+ Error.captureStackTrace(this, arguments.callee);
+ this.name = 'ValidationError';
+ this.errors = instance.errors = {};
+};
+
+ValidationError.prototype.toString = function () {
+ return this.name + ': ' + Object.keys(this.errors).map(function (key) {
+ return String(this.errors[key]);
+ }, this).join(', ');
+};
+
+/**
+ * Inherits from MongooseError.
+ */
+
+ValidationError.prototype.__proto__ = MongooseError.prototype;
+
+Document.ValidationError = ValidationError;
+
+/**
+ * Document Error
+ *
+ * @param text
+ */
+
+function DocumentError () {
+ MongooseError.call(this, msg);
+ Error.captureStackTrace(this, arguments.callee);
+ this.name = 'DocumentError';
+};
+
+/**
+ * Inherits from MongooseError.
+ */
+
+DocumentError.prototype.__proto__ = MongooseError.prototype;
+
+exports.Error = DocumentError;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js
new file mode 100644
index 0000000..52e6a03
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js
@@ -0,0 +1,8 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Binary = require('mongodb').BSONPure.Binary;
+
+module.exports = exports = Binary;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js
new file mode 100644
index 0000000..06176da
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js
@@ -0,0 +1,176 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Collection = require('../../collection')
+ , NativeCollection = require('mongodb').Collection
+ , utils = require('../../utils')
+
+/**
+ * Native collection
+ *
+ * @api private
+ */
+
+function MongooseCollection () {
+ Collection.apply(this, arguments);
+};
+
+/**
+ * Inherit from abstract Collection.
+ */
+
+MongooseCollection.prototype.__proto__ = Collection.prototype;
+
+/**
+ * Called when the connection opens
+ *
+ * @api private
+ */
+
+MongooseCollection.prototype.onOpen = function () {
+ var self = this;
+ this.conn.db.collection(this.name, function (err, collection) {
+ if (err) {
+ // likely a strict mode error
+ self.conn.emit('error', err);
+ } else {
+ self.collection = collection;
+ Collection.prototype.onOpen.call(self);
+ }
+ });
+};
+
+/**
+ * Called when the connection closes
+ *
+ * @api private
+ */
+
+MongooseCollection.prototype.onClose = function () {
+ Collection.prototype.onClose.call(this);
+};
+
+/**
+ * Copy the collection methods and make them subject to queues
+ */
+
+for (var i in NativeCollection.prototype)
+ (function(i){
+ MongooseCollection.prototype[i] = function () {
+ // BENCHMARKME: is it worth caching the prototype methods? probably
+ if (!this.buffer) {
+ var collection = this.collection
+ , args = arguments
+ , self = this;
+
+ process.nextTick(function(){
+ var debug = self.conn.base.options.debug;
+
+ if (debug) {
+ if ('function' === typeof debug) {
+ debug.apply(debug
+ , [self.name, i].concat(utils.args(args, 0, args.length-1)));
+ } else {
+ console.error('\x1B[0;36mMongoose:\x1B[0m %s.%s(%s) %s %s %s'
+ , self.name
+ , i
+ , print(args[0])
+ , print(args[1])
+ , print(args[2])
+ , print(args[3]))
+ }
+ }
+
+ collection[i].apply(collection, args);
+ });
+ } else {
+ this.addQueue(i, arguments);
+ }
+ };
+ })(i);
+
+/**
+ * Debug print helper
+ * @private
+ */
+
+function print (arg) {
+ var type = typeof arg;
+ if ('function' === type || 'undefined' === type) return '';
+ return format(arg);
+}
+
+/**
+ * Debug print helper
+ * @private
+ */
+
+function format (obj, sub) {
+ var x = utils.clone(obj);
+ if (x) {
+ if ('Binary' === x.constructor.name) {
+ x = '[object Buffer]';
+ } else if ('Object' === x.constructor.name) {
+ var keys = Object.keys(x)
+ , i = keys.length
+ , key
+ while (i--) {
+ key = keys[i];
+ if (x[key]) {
+ if ('Binary' === x[key].constructor.name) {
+ x[key] = '[object Buffer]';
+ } else if ('Object' === x[key].constructor.name) {
+ x[key] = format(x[key], true);
+ } else if (Array.isArray(x[key])) {
+ x[key] = x[key].map(function (o) {
+ return format(o, true)
+ });
+ }
+ }
+ }
+ }
+ if (sub) return x;
+ }
+
+ return require('util')
+ .inspect(x, false, 10, true)
+ .replace(/\n/g, '')
+ .replace(/\s{2,}/g, ' ')
+}
+
+/**
+ * Implement getIndexes
+ *
+ * @param {Function} callback
+ * @api private
+ */
+
+MongooseCollection.prototype.getIndexes =
+MongooseCollection.prototype.indexInformation;
+
+/**
+ * Override signature of ensureIndex. -native one is not standard.
+ *
+ * @param {Object} spec
+ * @param {Object} options
+ * @param {Function} callback
+ * @api public
+ */
+
+var oldEnsureIndex = NativeCollection.prototype.ensureIndex;
+
+function noop () {};
+
+NativeCollection.prototype.ensureIndex = function(fields, options, fn){
+ if (!this.buffer) {
+ return oldEnsureIndex.apply(this, arguments);
+ }
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = MongooseCollection;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js
new file mode 100644
index 0000000..3becd1b
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js
@@ -0,0 +1,106 @@
+/**
+ * Module dependencies.
+ */
+
+var Connection = require('../../connection')
+ , mongo = require('mongodb')
+ , Server = mongo.Server
+ , ReplSetServers = mongo.ReplSetServers;
+
+/**
+ * Connection for mongodb-native driver
+ *
+ * @api private
+ */
+
+function NativeConnection() {
+ Connection.apply(this, arguments);
+};
+
+/**
+ * Inherits from Connection.
+ */
+
+NativeConnection.prototype.__proto__ = Connection.prototype;
+
+/**
+ * Opens the connection.
+ *
+ * Example server options:
+ * auto_reconnect (default: false)
+ * poolSize (default: 1)
+ *
+ * Example db options:
+ * pk - custom primary key factory to generate `_id` values
+ *
+ * Some of these may break Mongoose. Use at your own risk. You have been warned.
+ *
+ * @param {Function} callback
+ * @api private
+ */
+
+NativeConnection.prototype.doOpen = function (fn) {
+ var server;
+
+ if (!this.db) {
+ server = new mongo.Server(this.host, Number(this.port), this.options.server);
+ this.db = new mongo.Db(this.name, server, this.options.db);
+ }
+
+ this.db.open(fn);
+
+ return this;
+};
+
+/**
+ * Opens a set connection
+ *
+ * See description of doOpen for server options. In this case options.replset
+ * is also passed to ReplSetServers. Some additional options there are
+ *
+ * reconnectWait (default: 1000)
+ * retries (default: 30)
+ * rs_name (default: false)
+ * read_secondary (default: false) Are reads allowed from secondaries?
+ *
+ * @param {Function} fn
+ * @api private
+ */
+
+NativeConnection.prototype.doOpenSet = function (fn) {
+ if (!this.db) {
+ var servers = []
+ , ports = this.port
+ , self = this
+
+ this.host.forEach(function (host, i) {
+ servers.push(new mongo.Server(host, Number(ports[i]), self.options.server));
+ });
+
+ var server = new ReplSetServers(servers, this.options.replset);
+ this.db = new mongo.Db(this.name, server, this.options.db);
+ }
+
+ this.db.open(fn);
+
+ return this;
+};
+
+/**
+ * Closes the connection
+ *
+ * @param {Function} callback
+ * @api private
+ */
+
+NativeConnection.prototype.doClose = function (fn) {
+ this.db.close();
+ if (fn) fn();
+ return this;
+}
+
+/**
+ * Module exports.
+ */
+
+module.exports = NativeConnection;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js
new file mode 100644
index 0000000..ee6d598
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js
@@ -0,0 +1,43 @@
+
+/**
+ * Module dependencies.
+ */
+
+var ObjectId = require('mongodb').BSONPure.ObjectID;
+
+/**
+ * Constructor export
+ *
+ * @api private
+ */
+
+var ObjectIdToString = ObjectId.toString.bind(ObjectId);
+
+module.exports = exports = ObjectId;
+/**
+ * Creates an ObjectID for this driver
+ *
+ * @param {Object} hex string or ObjectId
+ * @api private
+ */
+
+exports.fromString = function(str){
+ // patch native driver bug in V0.9.6.4
+ if (!('string' === typeof str && 24 === str.length)) {
+ throw new Error("Invalid ObjectId");
+ }
+
+ return ObjectId.createFromHexString(str);
+};
+
+/**
+ * Gets an ObjectId and converts it to string.
+ *
+ * @param {ObjectId} -native objectid
+ * @api private
+ */
+
+exports.toString = function(oid){
+ if (!arguments.length) return ObjectIdToString();
+ return oid.toHexString();
+};
diff --git a/node_modules/mongoose/lib/error.js b/node_modules/mongoose/lib/error.js
new file mode 100644
index 0000000..bd4ee61
--- /dev/null
+++ b/node_modules/mongoose/lib/error.js
@@ -0,0 +1,25 @@
+
+/**
+ * Mongoose error
+ *
+ * @api private
+ */
+
+function MongooseError (msg) {
+ Error.call(this);
+ Error.captureStackTrace(this, arguments.callee);
+ this.message = msg;
+ this.name = 'MongooseError';
+};
+
+/**
+ * Inherits from Error.
+ */
+
+MongooseError.prototype.__proto__ = Error.prototype;
+
+/**
+ * Module exports.
+ */
+
+module.exports = MongooseError;
diff --git a/node_modules/mongoose/lib/index.js b/node_modules/mongoose/lib/index.js
new file mode 100644
index 0000000..6fd449e
--- /dev/null
+++ b/node_modules/mongoose/lib/index.js
@@ -0,0 +1,368 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Schema = require('./schema')
+ , SchemaType = require('./schematype')
+ , VirtualType = require('./virtualtype')
+ , SchemaTypes = Schema.Types
+ , SchemaDefaults = require('./schemadefault')
+ , Types = require('./types')
+ , Query = require('./query')
+ , Promise = require('./promise')
+ , Model = require('./model')
+ , Document = require('./document')
+ , utils = require('./utils');
+
+/**
+ * Mongoose constructor. Most apps will only use one instance.
+ *
+ * @api public
+ */
+
+function Mongoose () {
+ this.connections = [];
+ this.plugins = [];
+ this.models = {};
+ this.modelSchemas = {};
+ this.options = {};
+ this.createConnection(); // default connection
+};
+
+/**
+ * Sets/gets mongoose options
+ *
+ * Examples:
+ * mongoose.set('test') // returns the 'test' value
+ * mongoose.set('test', value) // sets the 'test' value
+ *
+ * @param {String} key
+ * @param {String} value
+ * @api public
+ */
+
+Mongoose.prototype.set =
+Mongoose.prototype.get = function (key, value) {
+ if (arguments.length == 1)
+ return this.options[key];
+ this.options[key] = value;
+ return this;
+};
+
+/**
+ * Creates a Connection instance.
+ *
+ * Examples:
+ *
+ * // with mongodb:// URI
+ * db = mongoose.createConnection('mongodb://localhost:port/database');
+ *
+ * // with [host, database_name[, port] signature
+ * db = mongoose.createConnection('localhost', 'database', port)
+ *
+ * // initialize now, connect later
+ * db = mongoose.createConnection();
+ * db.open('localhost', 'database', port);
+ *
+ * @param {String} mongodb:// URI
+ * @return {Connection} the created Connection object
+ * @api public
+ */
+
+Mongoose.prototype.createConnection = function () {
+ var conn = new Connection(this);
+ this.connections.push(conn);
+ if (arguments.length)
+ conn.open.apply(conn, arguments);
+ return conn;
+};
+
+/**
+ * Creates a replica set connection
+ *
+ * @see {Mongoose#createConnection}
+ * @api public
+ */
+
+Mongoose.prototype.createSetConnection = function () {
+ var conn = new Connection(this);
+ this.connections.push(conn);
+ if (arguments.length)
+ conn.openSet.apply(conn, arguments);
+ return conn;
+};
+
+/**
+ * Connects the default mongoose connection
+ *
+ * @see {Mongoose#createConnection}
+ * @api public
+ */
+
+Mongoose.prototype.connect = function (){
+ this.connection.open.apply(this.connection, arguments);
+ return this;
+};
+
+/**
+ * Connects the default mongoose connection to a replica set
+ *
+ * @see {Mongoose#createConnection}
+ * @api public
+ */
+
+Mongoose.prototype.connectSet = function (){
+ this.connection.openSet.apply(this.connection, arguments);
+ return this;
+};
+
+/**
+ * Disconnects from all connections.
+ *
+ * @param {Function} optional callback
+ * @api public
+ */
+
+Mongoose.prototype.disconnect = function (fn) {
+ var count = this.connections.length;
+ this.connections.forEach(function(conn){
+ conn.close(function(err){
+ if (err) return fn(err);
+ if (fn)
+ --count || fn();
+ });
+ });
+ return this;
+};
+
+/**
+ * Defines a model or retrieves it
+ *
+ * @param {String} model name
+ * @param {Schema} schema object
+ * @param {String} collection name (optional, induced from model name)
+ * @param {Boolean} whether to skip initialization (defaults to false)
+ * @api public
+ */
+
+Mongoose.prototype.model = function (name, schema, collection, skipInit) {
+ // normalize collection
+ if (!(schema instanceof Schema)) {
+ collection = schema;
+ schema = false;
+ }
+
+ if ('boolean' === typeof collection) {
+ skipInit = collection;
+ collection = null;
+ }
+
+ // look up models for the collection
+ if (!this.modelSchemas[name]) {
+ if (!schema && name in SchemaDefaults) {
+ schema = SchemaDefaults[name];
+ }
+
+ if (schema) {
+ this.modelSchemas[name] = schema;
+ for (var i = 0, l = this.plugins.length; i < l; i++) {
+ schema.plugin(this.plugins[i][0], this.plugins[i][1]);
+ }
+ } else {
+ throw new Error('Schema hasn\'t been registered for model "' + name + '".\n'
+ + 'Use mongoose.model(name, schema)');
+ }
+ }
+
+ if (!schema) {
+ schema = this.modelSchemas[name];
+ }
+
+ if (!collection) {
+ collection = schema.set('collection') || utils.toCollectionName(name);
+ }
+
+ if (!this.models[name]) {
+ var model = Model.compile(name
+ , this.modelSchemas[name]
+ , collection
+ , this.connection
+ , this);
+
+ if (!skipInit) model.init();
+
+ this.models[name] = model;
+ }
+
+ return this.models[name];
+};
+
+/**
+ * Declares a plugin executed on Schemas. Equivalent to calling `.plugin(fn)`
+ * on each Schema you create.
+ *
+ * @param {Function} plugin callback
+ * @api public
+ */
+
+Mongoose.prototype.plugin = function (fn, opts) {
+ this.plugins.push([fn, opts]);
+ return this;
+};
+
+/**
+ * Default connection
+ *
+ * @api public
+ */
+
+Mongoose.prototype.__defineGetter__('connection', function(){
+ return this.connections[0];
+});
+
+/**
+ * Driver depentend APIs
+ */
+
+var driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';
+
+/**
+ * Connection
+ *
+ * @api public
+ */
+
+var Connection = require(driver + '/connection');
+
+/**
+ * Collection
+ *
+ * @api public
+ */
+
+var Collection = require(driver + '/collection');
+
+/**
+ * Export default singleton.
+ *
+ * @api public
+ */
+
+module.exports = exports = new Mongoose();
+
+/**
+ * Collection
+ *
+ * @api public
+ */
+
+exports.Collection = Collection;
+
+/**
+ * Connection
+ *
+ * @api public
+ */
+
+exports.Connection = Connection;
+
+/**
+ * Exports Mongoose version
+ *
+ * @param version
+ */
+
+exports.version = JSON.parse(
+ require('fs').readFileSync(__dirname + '/../package.json', 'utf8')
+).version;
+
+/**
+ * Export Mongoose constructor
+ *
+ * @api public
+ */
+
+exports.Mongoose = Mongoose;
+
+/**
+ * Export Schema constructor
+ *
+ * @api public
+ */
+
+exports.Schema = Schema;
+
+/**
+ * Export SchemaType constructor.
+ *
+ * @api public
+ */
+
+exports.SchemaType = SchemaType;
+
+/**
+ * Export VirtualType constructor.
+ *
+ * @api public
+ */
+
+exports.VirtualType = VirtualType;
+
+/**
+ * Export Schema types
+ *
+ * @api public
+ */
+
+exports.SchemaTypes = SchemaTypes;
+
+/**
+ * Export types
+ *
+ * @api public
+ */
+
+exports.Types = Types;
+
+/**
+ * Export Query
+ *
+ * @api public
+ */
+
+exports.Query = Query;
+
+/**
+ * Export Promise
+ *
+ * @api public
+ */
+
+exports.Promise = Promise;
+
+/**
+ * Export Model constructor
+ *
+ * @api public
+ */
+
+exports.Model = Model;
+
+/**
+ * Export Document constructor
+ *
+ * @api public
+ */
+
+exports.Document = Document;
+
+/**
+ * Export MongooseError
+ *
+ * @api public
+ */
+
+exports.Error = require('./error');
+
+exports.mongo = require('mongodb');
diff --git a/node_modules/mongoose/lib/model.js b/node_modules/mongoose/lib/model.js
new file mode 100644
index 0000000..75bfda3
--- /dev/null
+++ b/node_modules/mongoose/lib/model.js
@@ -0,0 +1,917 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Document = require('./document')
+ , MongooseArray = require('./types/array')
+ , MongooseBuffer = require('./types/buffer')
+ , MongooseError = require('./error')
+ , Query = require('./query')
+ , utils = require('./utils')
+ , isMongooseObject = utils.isMongooseObject
+ , EventEmitter = utils.EventEmitter
+ , merge = utils.merge
+ , Promise = require('./promise')
+ , tick = utils.tick
+
+/**
+ * Model constructor
+ *
+ * @param {Object} values to set
+ * @api public
+ */
+
+function Model (doc, fields) {
+ Document.call(this, doc, fields);
+};
+
+/**
+ * Inherits from Document.
+ */
+
+Model.prototype.__proto__ = Document.prototype;
+
+/**
+ * Connection the model uses. Set by the Connection or if absent set to the
+ * default mongoose connection;
+ *
+ * @api public
+ */
+
+Model.prototype.db;
+
+/**
+ * Collection the model uses. Set by Mongoose instance
+ *
+ * @api public
+ */
+
+Model.prototype.collection;
+
+/**
+ * Model name.
+ *
+ * @api public
+ */
+
+Model.prototype.modelName;
+
+/**
+ * Returns what paths can be populated
+ *
+ * @param {query} query object
+ * @return {Object] population paths
+ * @api private
+ */
+
+Model.prototype._getPopulationKeys = function getPopulationKeys (query) {
+ if (!(query && query.options.populate)) return;
+
+ var names = Object.keys(query.options.populate)
+ , n = names.length
+ , name
+ , paths = {}
+ , hasKeys
+ , schema
+
+ while (n--) {
+ name = names[n];
+ schema = this.schema.path(name);
+ hasKeys = true;
+
+ if (!schema) {
+ // if the path is not recognized, it's potentially embedded docs
+ // walk path atoms from right to left to find a matching path
+ var pieces = name.split('.')
+ , i = pieces.length;
+
+ while (i--) {
+ var path = pieces.slice(0, i).join('.')
+ , pathSchema = this.schema.path(path);
+
+ // loop until we find an array schema
+ if (pathSchema && pathSchema.caster) {
+ if (!paths[path]) {
+ paths[path] = { sub: {} };
+ }
+
+ paths[path].sub[pieces.slice(i).join('.')] = query.options.populate[name];
+ hasKeys || (hasKeys = true);
+ break;
+ }
+ }
+ } else {
+ paths[name] = query.options.populate[name];
+ hasKeys || (hasKeys = true);
+ }
+ }
+
+ return hasKeys && paths;
+};
+
+/**
+ * Populates an object
+ *
+ * @param {SchemaType} schema type for the oid
+ * @param {Object} object id or array of object ids
+ * @param {Object} object specifying query conditions, fields, and options
+ * @param {Function} callback
+ * @api private
+ */
+
+Model.prototype._populate = function populate (schema, oid, query, fn) {
+ if (!Array.isArray(oid)) {
+ var conditions = query.conditions || {};
+ conditions._id = oid;
+
+ return this
+ .model(schema.options.ref)
+ .findOne(conditions, query.fields, query.options, fn);
+ }
+
+ if (!oid.length) {
+ return fn(null, oid);
+ }
+
+ var model = this.model(schema.caster.options.ref)
+ , conditions = query && query.conditions || {};
+ conditions._id || (conditions._id = { $in: oid });
+
+ model.find(conditions, query.fields, query.options, function (err, docs) {
+ if (err) return fn(err);
+
+ // user specified sort order?
+ if (query.options && query.options.sort) {
+ return fn(null, docs);
+ }
+
+ // put back in original id order (using a hash reduces complexity from n*n to 2n)
+ var docHash = {};
+ docs.forEach(function (doc) {
+ docHash[doc._id] = doc;
+ });
+
+ var arr = [];
+ oid.forEach(function (id) {
+ if (id in docHash) arr.push(docHash[id]);
+ });
+
+ fn(null, arr);
+ });
+};
+
+/**
+ * Performs auto-population of relations.
+ *
+ * @param {Object} document returned by mongo
+ * @param {Query} query that originated the initialization
+ * @param {Function} callback
+ * @api private
+ */
+
+Model.prototype.init = function init (doc, query, fn) {
+ if ('function' == typeof query) {
+ fn = query;
+ query = null;
+ }
+
+ var populate = this._getPopulationKeys(query);
+
+ if (!populate) {
+ return Document.prototype.init.call(this, doc, fn);
+ }
+
+ // population from other models is necessary
+ var self = this;
+
+ init(doc, '', function (err) {
+ if (err) return fn(err);
+ Document.prototype.init.call(self, doc, fn);
+ });
+
+ return this;
+
+ function init (obj, prefix, fn) {
+ prefix = prefix || '';
+
+ var keys = Object.keys(obj)
+ , len = keys.length;
+
+ function next () {
+ if (--len < 0) return fn();
+
+ var i = keys[len]
+ , path = prefix + i
+ , schema = self.schema.path(path)
+ , total = 0
+ , poppath
+
+ if (!schema && obj[i] && 'Object' === obj[i].constructor.name) {
+ // assume nested object
+ return init(obj[i], path + '.', next);
+ }
+
+ if (!(obj[i] && schema && populate[path])) return next();
+
+ // this query object is re-used and passed around. we clone
+ // it to prevent query condition contamination between
+ // one populate call to the next.
+ poppath = utils.clone(populate[path]);
+
+ if (poppath.sub) {
+ obj[i].forEach(function (subobj) {
+ var pkeys = Object.keys(poppath.sub)
+ , pi = pkeys.length
+ , key
+
+ while (pi--) {
+ key = pkeys[pi];
+
+ if (subobj[key]) (function (key) {
+
+ total++;
+ self._populate(schema.schema.path(key), subobj[key], poppath.sub[key], done);
+ function done (err, doc) {
+ if (err) return error(err);
+ subobj[key] = doc;
+ --total || next();
+ }
+ })(key);
+ }
+ });
+
+ if (0 === total) return next();
+
+ } else {
+ self._populate(schema, obj[i], poppath, function (err, doc) {
+ if (err) return error(err);
+ obj[i] = doc;
+ next();
+ });
+ }
+ };
+
+ next();
+ };
+
+ function error (err) {
+ if (error.err) return;
+ fn(error.err = err);
+ }
+};
+
+function handleSave (promise, self) {
+ return tick(function handleSave (err, result) {
+ if (err) return promise.error(err);
+
+ self._storeShard();
+
+ var numAffected;
+ if (result) {
+ numAffected = result.length
+ ? result.length
+ : result;
+ } else {
+ numAffected = 0;
+ }
+
+ self.emit('save', self, numAffected);
+ promise.complete(self, numAffected);
+ promise = null;
+ self = null;
+ });
+}
+
+/**
+ * Saves this document.
+ *
+ * @see Model#registerHooks
+ * @param {Function} fn
+ * @api public
+ */
+
+Model.prototype.save = function save (fn) {
+ var promise = new Promise(fn)
+ , complete = handleSave(promise, this)
+ , options = {}
+
+ if (this.options.safe) {
+ options.safe = this.options.safe;
+ }
+
+ if (this.isNew) {
+ // send entire doc
+ this.collection.insert(this.toObject({ depopulate: 1 }), options, complete);
+ this._reset();
+ this.isNew = false;
+ this.emit('isNew', false);
+
+ } else {
+ var delta = this._delta();
+ this._reset();
+
+ if (delta) {
+ var where = this._where();
+ this.collection.update(where, delta, options, complete);
+ } else {
+ complete(null);
+ }
+
+ this.emit('isNew', false);
+ }
+};
+
+/**
+ * Produces a special query document of the modified properties.
+ * @api private
+ */
+
+Model.prototype._delta = function _delta () {
+ var dirty = this._dirty();
+
+ if (!dirty.length) return;
+
+ var self = this
+ , useSet = this.options['use$SetOnSave'];
+
+ return dirty.reduce(function (delta, data) {
+ var type = data.value
+ , schema = data.schema
+ , atomics
+ , val
+ , obj
+
+ if (type === undefined) {
+ if (!delta.$unset) delta.$unset = {};
+ delta.$unset[data.path] = 1;
+
+ } else if (type === null) {
+ if (!delta.$set) delta.$set = {};
+ delta.$set[data.path] = type;
+
+ } else if (type._path && type.doAtomics) {
+ // a MongooseArray or MongooseNumber
+ atomics = type._atomics;
+
+ var ops = Object.keys(atomics)
+ , i = ops.length
+ , op;
+
+ while (i--) {
+ op = ops[i]
+
+ if (op === '$pushAll' || op === '$pullAll') {
+ if (atomics[op].length === 1) {
+ val = atomics[op][0];
+ delete atomics[op];
+ op = op.replace('All', '');
+ atomics[op] = val;
+ }
+ }
+
+ val = atomics[op];
+ obj = delta[op] = delta[op] || {};
+
+ if (op === '$pull' || op === '$push') {
+ if ('Object' !== val.constructor.name) {
+ if (Array.isArray(val)) val = [val];
+ // TODO Should we place pull and push casting into the pull and push methods?
+ val = schema.cast(val)[0];
+ }
+ }
+
+ obj[data.path] = isMongooseObject(val)
+ ? val.toObject({ depopulate: 1 }) // MongooseArray
+ : Array.isArray(val)
+ ? val.map(function (mem) {
+ return isMongooseObject(mem)
+ ? mem.toObject({ depopulate: 1 })
+ : mem.valueOf
+ ? mem.valueOf()
+ : mem;
+ })
+ : val.valueOf
+ ? val.valueOf() // Numbers
+ : val;
+
+ if ('$addToSet' === op) {
+ if (val.length > 1) {
+ obj[data.path] = { $each: obj[data.path] };
+ } else {
+ obj[data.path] = obj[data.path][0];
+ }
+ }
+ }
+ } else {
+ if (type instanceof MongooseArray ||
+ type instanceof MongooseBuffer) {
+ type = type.toObject({ depopulate: 1 });
+ } else if (type._path) {
+ type = type.valueOf();
+ } else {
+ // nested object literal
+ type = utils.clone(type);
+ }
+
+ if (useSet) {
+ if (!('$set' in delta))
+ delta['$set'] = {};
+
+ delta['$set'][data.path] = type;
+ } else
+ delta[data.path] = type;
+ }
+
+ return delta;
+ }, {});
+}
+
+/**
+ * _where
+ *
+ * Returns a query object which applies shardkeys if
+ * they exist.
+ *
+ * @private
+ */
+
+Model.prototype._where = function _where () {
+ var where = {};
+
+ if (this._shardval) {
+ var paths = Object.keys(this._shardval)
+ , len = paths.length
+
+ for (var i = 0; i < len; ++i) {
+ where[paths[i]] = this._shardval[paths[i]];
+ }
+ }
+
+ var id = this._doc._id.valueOf // MongooseNumber
+ ? this._doc._id.valueOf()
+ : this._doc._id;
+
+ where._id = id;
+ return where;
+}
+
+/**
+ * Remove the document
+ *
+ * @param {Function} callback
+ * @api public
+ */
+
+Model.prototype.remove = function remove (fn) {
+ if (this._removing) return this;
+
+ var promise = this._removing = new Promise(fn)
+ , where = this._where()
+ , self = this;
+
+ this.collection.remove(where, tick(function (err) {
+ if (err) {
+ this._removing = null;
+ return promise.error(err);
+ }
+ promise.complete();
+ self.emit('remove', self);
+ }));
+
+ return this;
+};
+
+/**
+ * Register hooks override
+ *
+ * @api private
+ */
+
+Model.prototype._registerHooks = function registerHooks () {
+ Document.prototype._registerHooks.call(this);
+};
+
+/**
+ * Shortcut to access another model.
+ *
+ * @param {String} model name
+ * @api public
+ */
+
+Model.prototype.model = function model (name) {
+ return this.db.model(name);
+};
+
+/**
+ * Access the options defined in the schema
+ *
+ * @api private
+ */
+
+Model.prototype.__defineGetter__('options', function () {
+ return this.schema ? this.schema.options : {};
+});
+
+/**
+ * Give the constructor the ability to emit events.
+ */
+
+for (var i in EventEmitter.prototype)
+ Model[i] = EventEmitter.prototype[i];
+
+/**
+ * Called when the model compiles
+ *
+ * @api private
+ */
+
+Model.init = function init () {
+ // build indexes
+ var self = this
+ , indexes = this.schema.indexes
+ , safe = this.schema.options.safe
+ , count = indexes.length;
+
+ indexes.forEach(function (index) {
+ var options = index[1];
+ options.safe = safe;
+ self.collection.ensureIndex(index[0], options, tick(function (err) {
+ if (err) return self.db.emit('error', err);
+ --count || self.emit('index');
+ }));
+ });
+
+ this.schema.emit('init', this);
+};
+
+/**
+ * Document schema
+ *
+ * @api public
+ */
+
+Model.schema;
+
+/**
+ * Database instance the model uses.
+ *
+ * @api public
+ */
+
+Model.db;
+
+/**
+ * Collection the model uses.
+ *
+ * @api public
+ */
+
+Model.collection;
+
+/**
+ * Define properties that access the prototype.
+ */
+
+['db', 'collection', 'schema', 'options', 'model'].forEach(function(prop){
+ Model.__defineGetter__(prop, function(){
+ return this.prototype[prop];
+ });
+});
+
+/**
+ * Module exports.
+ */
+
+module.exports = exports = Model;
+
+Model.remove = function remove (conditions, callback) {
+ if ('function' === typeof conditions) {
+ callback = conditions;
+ conditions = {};
+ }
+
+ var query = new Query(conditions).bind(this, 'remove');
+
+ if ('undefined' === typeof callback)
+ return query;
+
+ this._applyNamedScope(query);
+ return query.remove(callback);
+};
+
+/**
+ * Finds documents
+ *
+ * Examples:
+ * // retrieve only certain keys
+ * MyModel.find({ name: /john/i }, ['name', 'friends'], function () { })
+ *
+ * // pass options
+ * MyModel.find({ name: /john/i }, [], { skip: 10 } )
+ *
+ * @param {Object} conditions
+ * @param {Object/Function} (optional) fields to hydrate or callback
+ * @param {Function} callback
+ * @api public
+ */
+
+Model.find = function find (conditions, fields, options, callback) {
+ if ('function' == typeof conditions) {
+ callback = conditions;
+ conditions = {};
+ fields = null;
+ options = null;
+ } else if ('function' == typeof fields) {
+ callback = fields;
+ fields = null;
+ options = null;
+ } else if ('function' == typeof options) {
+ callback = options;
+ options = null;
+ }
+
+ var query = new Query(conditions, options);
+ query.bind(this, 'find');
+ query.select(fields);
+
+ if ('undefined' === typeof callback)
+ return query;
+
+ this._applyNamedScope(query);
+ return query.find(callback);
+};
+
+/**
+ * Merges the current named scope query into `query`.
+ *
+ * @param {Query} query
+ * @api private
+ */
+
+Model._applyNamedScope = function _applyNamedScope (query) {
+ var cQuery = this._cumulativeQuery;
+
+ if (cQuery) {
+ merge(query._conditions, cQuery._conditions);
+ if (query._fields && cQuery._fields)
+ merge(query._fields, cQuery._fields);
+ if (query.options && cQuery.options)
+ merge(query.options, cQuery.options);
+ delete this._cumulativeQuery;
+ }
+
+ return query;
+}
+
+/**
+ * Finds by id
+ *
+ * @param {ObjectId/Object} objectid, or a value that can be casted to it
+ * @api public
+ */
+
+Model.findById = function findById (id, fields, options, callback) {
+ return this.findOne({ _id: id }, fields, options, callback);
+};
+
+/**
+ * Finds one document
+ *
+ * @param {Object} conditions
+ * @param {Object/Function} (optional) fields to hydrate or callback
+ * @param {Function} callback
+ * @api public
+ */
+
+Model.findOne = function findOne (conditions, fields, options, callback) {
+ if ('function' == typeof options) {
+ // TODO Handle all 3 of the following scenarios
+ // Hint: Only some of these scenarios are possible if cQuery is present
+ // Scenario: findOne(conditions, fields, callback);
+ // Scenario: findOne(fields, options, callback);
+ // Scenario: findOne(conditions, options, callback);
+ callback = options;
+ options = null;
+ } else if ('function' == typeof fields) {
+ // TODO Handle all 2 of the following scenarios
+ // Scenario: findOne(conditions, callback)
+ // Scenario: findOne(fields, callback)
+ // Scenario: findOne(options, callback);
+ callback = fields;
+ fields = null;
+ options = null;
+ } else if ('function' == typeof conditions) {
+ callback = conditions;
+ conditions = {};
+ fields = null;
+ options = null;
+ }
+
+ var query = new Query(conditions, options).select(fields).bind(this, 'findOne');
+
+ if ('undefined' == typeof callback)
+ return query;
+
+ this._applyNamedScope(query);
+ return query.findOne(callback);
+};
+
+/**
+ * Counts documents
+ *
+ * @param {Object} conditions
+ * @param {Function} optional callback
+ * @api public
+ */
+
+Model.count = function count (conditions, callback) {
+ if ('function' === typeof conditions)
+ callback = conditions, conditions = {};
+
+ var query = new Query(conditions).bind(this, 'count');
+ if ('undefined' == typeof callback)
+ return query;
+
+ this._applyNamedScope(query);
+ return query.count(callback);
+};
+
+Model.distinct = function distinct (field, conditions, callback) {
+ var query = new Query(conditions).bind(this, 'distinct');
+ if ('undefined' == typeof callback) {
+ query._distinctArg = field;
+ return query;
+ }
+
+ this._applyNamedScope(query);
+ return query.distinct(field, callback);
+};
+
+/**
+ * `where` enables a very nice sugary api for doing your queries.
+ * For example, instead of writing:
+ * User.find({age: {$gte: 21, $lte: 65}}, callback);
+ * we can instead write more readably:
+ * User.where('age').gte(21).lte(65);
+ * Moreover, you can also chain a bunch of these together like:
+ * User
+ * .where('age').gte(21).lte(65)
+ * .where('name', /^b/i) // All names that begin where b or B
+ * .where('friends').slice(10);
+ * @param {String} path
+ * @param {Object} val (optional)
+ * @return {Query}
+ * @api public
+ */
+
+Model.where = function where (path, val) {
+ var q = new Query().bind(this, 'find');
+ return q.where.apply(q, arguments);
+};
+
+/**
+ * Sometimes you need to query for things in mongodb using a JavaScript
+ * expression. You can do so via find({$where: javascript}), or you can
+ * use the mongoose shortcut method $where via a Query chain or from
+ * your mongoose Model.
+ *
+ * @param {String|Function} js is a javascript string or anonymous function
+ * @return {Query}
+ * @api public
+ */
+
+Model.$where = function $where () {
+ var q = new Query().bind(this, 'find');
+ return q.$where.apply(q, arguments);
+};
+
+/**
+ * Shortcut for creating a new Document that is automatically saved
+ * to the db if valid.
+ *
+ * @param {Object} doc
+ * @param {Function} callback
+ * @api public
+ */
+
+Model.create = function create (doc, fn) {
+ if (1 === arguments.length) {
+ return 'function' === typeof doc && doc(null);
+ }
+
+ var self = this
+ , docs = [null]
+ , promise
+ , count
+ , args
+
+ if (Array.isArray(doc)) {
+ args = doc;
+ } else {
+ args = utils.args(arguments, 0, arguments.length - 1);
+ fn = arguments[arguments.length - 1];
+ }
+
+ if (0 === args.length) return fn(null);
+
+ promise = new Promise(fn);
+ count = args.length;
+
+ args.forEach(function (arg, i) {
+ var doc = new self(arg);
+ docs[i+1] = doc;
+ doc.save(function (err) {
+ if (err) return promise.error(err);
+ --count || fn.apply(null, docs);
+ });
+ });
+
+ // TODO
+ // utilize collection.insertAll for batch processing?
+};
+
+/**
+ * Updates documents.
+ *
+ * Examples:
+ *
+ * MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);
+ * MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, fn);
+ *
+ * Valid options:
+ *
+ * - safe (boolean) safe mode (defaults to value set in schema (true))
+ * - upsert (boolean) whether to create the doc if it doesn't match (false)
+ * - multi (boolean) whether multiple documents should be updated (false)
+ *
+ * @param {Object} conditions
+ * @param {Object} doc
+ * @param {Object} options
+ * @param {Function} callback
+ * @return {Query}
+ * @api public
+ */
+
+Model.update = function update (conditions, doc, options, callback) {
+ if (arguments.length < 4) {
+ if ('function' === typeof options) {
+ // Scenario: update(conditions, doc, callback)
+ callback = options;
+ options = null;
+ } else if ('function' === typeof doc) {
+ // Scenario: update(doc, callback);
+ callback = doc;
+ doc = conditions;
+ conditions = {};
+ options = null;
+ }
+ }
+
+ var query = new Query(conditions, options).bind(this, 'update', doc);
+
+ if ('undefined' == typeof callback)
+ return query;
+
+ this._applyNamedScope(query);
+ return query.update(doc, callback);
+};
+
+/**
+ * Compiler utility.
+ *
+ * @param {String} model name
+ * @param {Schema} schema object
+ * @param {String} collection name
+ * @param {Connection} connection to use
+ * @param {Mongoose} mongoose instance
+ * @api private
+ */
+
+Model.compile = function compile (name, schema, collectionName, connection, base) {
+ // generate new class
+ function model () {
+ Model.apply(this, arguments);
+ };
+
+ model.modelName = name;
+ model.__proto__ = Model;
+ model.prototype.__proto__ = Model.prototype;
+ model.prototype.base = base;
+ model.prototype.schema = schema;
+ model.prototype.db = connection;
+ model.prototype.collection = connection.collection(collectionName);
+
+ // apply methods
+ for (var i in schema.methods)
+ model.prototype[i] = schema.methods[i];
+
+ // apply statics
+ for (var i in schema.statics)
+ model[i] = schema.statics[i];
+
+ // apply named scopes
+ if (schema.namedScopes) schema.namedScopes.compile(model);
+
+ return model;
+};
diff --git a/node_modules/mongoose/lib/namedscope.js b/node_modules/mongoose/lib/namedscope.js
new file mode 100644
index 0000000..1b3f5d4
--- /dev/null
+++ b/node_modules/mongoose/lib/namedscope.js
@@ -0,0 +1,70 @@
+var Query = require('./query');
+function NamedScope () {}
+
+NamedScope.prototype.query;
+
+NamedScope.prototype.where = function () {
+ var q = this.query || (this.query = new Query());
+ q.where.apply(q, arguments);
+ return q;
+};
+
+/**
+ * Decorate
+ *
+ * @param {NamedScope} target
+ * @param {Object} getters
+ * @api private
+ */
+
+NamedScope.prototype.decorate = function (target, getters) {
+ var name = this.name
+ , block = this.block
+ , query = this.query;
+ if (block) {
+ if (block.length === 0) {
+ Object.defineProperty(target, name, {
+ get: getters.block0(block)
+ });
+ } else {
+ target[name] = getters.blockN(block);
+ }
+ } else {
+ Object.defineProperty(target, name, {
+ get: getters.basic(query)
+ });
+ }
+};
+
+NamedScope.prototype.compile = function (model) {
+ var allScopes = this.scopesByName
+ , scope;
+ for (var k in allScopes) {
+ scope = allScopes[k];
+ scope.decorate(model, {
+ block0: function (block) {
+ return function () {
+ var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this));
+ block.call(cquery);
+ return this;
+ };
+ },
+ blockN: function (block) {
+ return function () {
+ var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this));
+ block.apply(cquery, arguments);
+ return this;
+ };
+ },
+ basic: function (query) {
+ return function () {
+ var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this));
+ cquery.find(query);
+ return this;
+ };
+ }
+ });
+ }
+};
+
+module.exports = NamedScope;
diff --git a/node_modules/mongoose/lib/promise.js b/node_modules/mongoose/lib/promise.js
new file mode 100644
index 0000000..c632a03
--- /dev/null
+++ b/node_modules/mongoose/lib/promise.js
@@ -0,0 +1,145 @@
+
+/**
+ * Module dependencies.
+ */
+
+var util = require('./utils');
+var EventEmitter = util.EventEmitter;
+
+/**
+ * Promise constructor.
+ *
+ * @param {Function} a callback+errback that takes err, ... as signature
+ * @api public
+ */
+
+function Promise (back) {
+ this.emitted = {};
+ if ('function' == typeof back)
+ this.addBack(back);
+};
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+Promise.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Adds an event or fires the callback right away.
+ *
+ * @return promise
+ * @api public
+ */
+
+Promise.prototype.on = function (event, callback) {
+ if (this.emitted[event])
+ callback.apply(this, this.emitted[event]);
+ else
+ EventEmitter.prototype.on.call(this, event, callback);
+
+ return this;
+};
+
+/**
+ * Keeps track of emitted events to run them on `on`
+ *
+ * @api private
+ */
+
+Promise.prototype.emit = function (event) {
+ // ensures a promise can't be complete() or error() twice
+ if (event == 'err' || event == 'complete'){
+ if (this.emitted.err || this.emitted.complete) {
+ return this;
+ }
+ this.emitted[event] = util.args(arguments, 1);
+ }
+
+ return EventEmitter.prototype.emit.apply(this, arguments);
+};
+
+/**
+ * Shortcut for emitting complete event
+ *
+ * @api public
+ */
+
+Promise.prototype.complete = function () {
+ var args = util.args(arguments);
+ return this.emit.apply(this, ['complete'].concat(args));
+};
+
+/**
+ * Shortcut for emitting err event
+ *
+ * @api public
+ */
+
+Promise.prototype.error = function () {
+ var args = util.args(arguments);
+ return this.emit.apply(this, ['err'].concat(args));
+};
+
+/**
+ * Shortcut for `.on('complete', fn)`
+ *
+ * @return promise
+ * @api public
+ */
+
+Promise.prototype.addCallback = function (fn) {
+ return this.on('complete', fn);
+};
+
+/**
+ * Shortcut for `.on('err', fn)`
+ *
+ * @return promise
+ * @api public
+ */
+
+Promise.prototype.addErrback = function (fn) {
+ return this.on('err', fn);
+};
+
+/**
+ * Adds a single function that's both callback and errback
+ *
+ * @return promise
+ * @api private
+ */
+
+Promise.prototype.addBack = function (fn) {
+ this.on('err', function(err){
+ fn.call(this, err);
+ });
+
+ this.on('complete', function(){
+ var args = util.args(arguments);
+ fn.apply(this, [null].concat(args));
+ });
+
+ return this;
+};
+
+/**
+ * Sugar for handling cases where you may be
+ * resolving to either an error condition or a
+ * success condition.
+ *
+ * @param {Error} optional error or null
+ * @param {Object} value to complete the promise with
+ * @api public
+ */
+
+Promise.prototype.resolve = function (err, val) {
+ if (err) return this.error(err);
+ return this.complete(val);
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = Promise;
diff --git a/node_modules/mongoose/lib/query.js b/node_modules/mongoose/lib/query.js
new file mode 100644
index 0000000..29c03ab
--- /dev/null
+++ b/node_modules/mongoose/lib/query.js
@@ -0,0 +1,1527 @@
+/**
+ * Module dependencies.
+ */
+
+var utils = require('./utils')
+ , merge = utils.merge
+ , Promise = require('./promise')
+ , Document = require('./document')
+ , inGroupsOf = utils.inGroupsOf
+ , tick = utils.tick
+ , QueryStream = require('./querystream')
+
+/**
+ * Query constructor
+ *
+ * @api private
+ */
+
+function Query (criteria, options) {
+ options = this.options = options || {};
+ this.safe = options.safe
+
+ // normalize population options
+ var pop = this.options.populate;
+ this.options.populate = {};
+
+ if (pop && Array.isArray(pop)) {
+ for (var i = 0, l = pop.length; i < l; i++) {
+ this.options.populate[pop[i]] = {};
+ }
+ }
+
+ this._conditions = {};
+ if (criteria) this.find(criteria);
+}
+
+/**
+ * Binds this query to a model.
+ * @param {Function} param
+ * @return {Query}
+ * @api public
+ */
+
+Query.prototype.bind = function bind (model, op, updateArg) {
+ this.model = model;
+ this.op = op;
+ if (op === 'update') this._updateArg = updateArg;
+ return this;
+};
+
+/**
+ * Executes the query returning a promise.
+ *
+ * Examples:
+ * query.run();
+ * query.run(callback);
+ * query.run('update');
+ * query.run('find', callback);
+ *
+ * @param {String|Function} op (optional)
+ * @param {Function} callback (optional)
+ * @return {Promise}
+ * @api public
+ */
+
+Query.prototype.run =
+Query.prototype.exec = function (op, callback) {
+ var promise = new Promise();
+
+ switch (typeof op) {
+ case 'function':
+ callback = op;
+ op = null;
+ break;
+ case 'string':
+ this.op = op;
+ break;
+ }
+
+ if (callback) promise.addBack(callback);
+
+ if (!this.op) {
+ promise.complete();
+ return promise;
+ }
+
+ if ('update' == this.op) {
+ this.update(this._updateArg, promise.resolve.bind(promise));
+ return promise;
+ }
+
+ if ('distinct' == this.op) {
+ this.distinct(this._distinctArg, promise.resolve.bind(promise));
+ return promise;
+ }
+
+ this[this.op](promise.resolve.bind(promise));
+ return promise;
+};
+
+/**
+ * Finds documents.
+ *
+ * @param {Object} criteria
+ * @param {Function} callback
+ * @api public
+ */
+
+Query.prototype.find = function (criteria, callback) {
+ this.op = 'find';
+ if ('function' === typeof criteria) {
+ callback = criteria;
+ criteria = {};
+ } else if (criteria instanceof Query) {
+ // TODO Merge options, too
+ merge(this._conditions, criteria._conditions);
+ } else if (criteria instanceof Document) {
+ merge(this._conditions, criteria.toObject());
+ } else if (criteria && 'Object' === criteria.constructor.name) {
+ merge(this._conditions, criteria);
+ }
+ if (!callback) return this;
+ return this.execFind(callback);
+};
+
+/**
+ * Casts obj, or if obj is not present, then this._conditions,
+ * based on the model's schema.
+ *
+ * @param {Function} model
+ * @param {Object} obj (optional)
+ * @api public
+ */
+
+Query.prototype.cast = function (model, obj) {
+ obj || (obj= this._conditions);
+
+ var schema = model.schema
+ , paths = Object.keys(obj)
+ , i = paths.length
+ , any$conditionals
+ , schematype
+ , nested
+ , path
+ , type
+ , val;
+
+ while (i--) {
+ path = paths[i];
+ val = obj[path];
+
+ if ('$or' === path || '$nor' === path) {
+ var k = val.length
+ , orComponentQuery;
+
+ while (k--) {
+ orComponentQuery = new Query(val[k]);
+ orComponentQuery.cast(model);
+ val[k] = orComponentQuery._conditions;
+ }
+
+ } else if (path === '$where') {
+ type = typeof val;
+
+ if ('string' !== type && 'function' !== type) {
+ throw new Error("Must have a string or function for $where");
+ }
+
+ if ('function' === type) {
+ obj[path] = val.toString();
+ }
+
+ continue;
+
+ } else {
+
+ if (!schema) {
+ // no casting for Mixed types
+ continue;
+ }
+
+ schematype = schema.path(path);
+
+ if (!schematype) {
+ // Handle potential embedded array queries
+ var split = path.split('.')
+ , j = split.length
+ , pathFirstHalf
+ , pathLastHalf
+ , remainingConds
+ , castingQuery;
+
+ // Find the part of the var path that is a path of the Schema
+ while (j--) {
+ pathFirstHalf = split.slice(0, j).join('.');
+ schematype = schema.path(pathFirstHalf);
+ if (schematype) break;
+ }
+
+ // If a substring of the input path resolves to an actual real path...
+ if (schematype) {
+ // Apply the casting; similar code for $elemMatch in schema/array.js
+ if (schematype.caster && schematype.caster.schema) {
+ remainingConds = {};
+ pathLastHalf = split.slice(j).join('.');
+ remainingConds[pathLastHalf] = val;
+ castingQuery = new Query(remainingConds);
+ castingQuery.cast(schematype.caster);
+ obj[path] = castingQuery._conditions[pathLastHalf];
+ } else {
+ obj[path] = val;
+ }
+ }
+
+ } else if (val === null || val === undefined) {
+ continue;
+ } else if ('Object' === val.constructor.name) {
+
+ any$conditionals = Object.keys(val).some(function (k) {
+ return k.charAt(0) === '$' && k !== '$id' && k !== '$ref';
+ });
+
+ if (!any$conditionals) {
+ obj[path] = schematype.castForQuery(val);
+ } else {
+
+ var ks = Object.keys(val)
+ , k = ks.length
+ , $cond;
+
+ while (k--) {
+ $cond = ks[k];
+ nested = val[$cond];
+
+ if ('$exists' === $cond) {
+ if ('boolean' !== typeof nested) {
+ throw new Error("$exists parameter must be Boolean");
+ }
+ continue;
+ }
+
+ if ('$type' === $cond) {
+ if ('number' !== typeof nested) {
+ throw new Error("$type parameter must be Number");
+ }
+ continue;
+ }
+
+ if ('$not' === $cond) {
+ this.cast(model, nested);
+ } else {
+ val[$cond] = schematype.castForQuery($cond, nested);
+ }
+ }
+ }
+ } else {
+ obj[path] = schematype.castForQuery(val);
+ }
+ }
+ }
+};
+
+/**
+ * Returns default options.
+ * @api private
+ */
+
+Query.prototype._optionsForExec = function (model) {
+ var options = utils.clone(this.options, { retainKeyOrder: true });
+ delete options.populate;
+ if (! ('safe' in options)) options.safe = model.options.safe;
+ return options;
+};
+
+/**
+ * Applies schematype selected options to this query.
+ * @api private
+ */
+
+Query.prototype._applyPaths = function applyPaths () {
+ // determine if query is selecting or excluding fields
+
+ var fields = this._fields
+ , exclude
+ , keys
+ , ki
+
+ if (fields) {
+ keys = Object.keys(fields);
+ ki = keys.length;
+
+ while (ki--) {
+ if ('_id' == keys[ki]) continue;
+ exclude = 0 === fields[keys[ki]];
+ break;
+ }
+ }
+
+ // if selecting, apply default schematype select:true fields
+ // if excluding, apply schematype select:false fields
+ // if not specified, apply both
+
+ var selected = []
+ , excluded = []
+
+ this.model.schema.eachPath(function (path, type) {
+ if ('boolean' != typeof type.selected) return;
+ ;(type.selected ? selected : excluded).push(path);
+ });
+
+ switch (exclude) {
+ case true:
+ this.exclude(excluded);
+ break;
+ case false:
+ this.select(selected);
+ break;
+ case undefined:
+ excluded.length && this.exclude(excluded);
+ selected.length && this.select(selected);
+ break;
+ }
+}
+
+/**
+ * Sometimes you need to query for things in mongodb using a JavaScript
+ * expression. You can do so via find({$where: javascript}), or you can
+ * use the mongoose shortcut method $where via a Query chain or from
+ * your mongoose Model.
+ *
+ * @param {String|Function} js is a javascript string or anonymous function
+ * @return {Query}
+ * @api public
+ */
+
+Query.prototype.$where = function (js) {
+ this._conditions['$where'] = js;
+ return this;
+};
+
+/**
+ * `where` enables a very nice sugary api for doing your queries.
+ * For example, instead of writing:
+ *
+ * User.find({age: {$gte: 21, $lte: 65}}, callback);
+ *
+ * we can instead write more readably:
+ *
+ * User.where('age').gte(21).lte(65);
+ *
+ * Moreover, you can also chain a bunch of these together like:
+ *
+ * User
+ * .where('age').gte(21).lte(65)
+ * .where('name', /^b/i) // All names that begin where b or B
+ * .where('friends').slice(10);
+ *
+ * @param {String} path
+ * @param {Object} val (optional)
+ * @return {Query}
+ * @api public
+ */
+
+Query.prototype.where = function (path, val) {
+ if (2 === arguments.length) {
+ this._conditions[path] = val;
+ }
+ this._currPath = path;
+ return this;
+};
+
+/**
+ * `equals` sugar.
+ *
+ * User.where('age').equals(49);
+ *
+ * Same as
+ *
+ * User.where('age', 49);
+ *
+ * @param {object} val
+ * @return {Query}
+ * @api public
+ */
+
+Query.prototype.equals = function equals (val) {
+ var path = this._currPath;
+ if (!path) throw new Error('equals() must be used after where()');
+ this._conditions[path] = val;
+ return this;
+}
+
+/**
+ * $or
+ */
+
+Query.prototype.or =
+Query.prototype.$or = function $or (array) {
+ var or = this._conditions.$or || (this._conditions.$or = []);
+ if (!Array.isArray(array)) array = [array];
+ or.push.apply(or, array);
+ return this;
+}
+
+/**
+ * $nor
+ */
+
+Query.prototype.nor =
+Query.prototype.$nor = function $nor (array) {
+ var nor = this._conditions.$nor || (this._conditions.$nor = []);
+ if (!Array.isArray(array)) array = [array];
+ nor.push.apply(nor, array);
+ return this;
+}
+
+/**
+ * $gt, $gte, $lt, $lte, $ne, $in, $nin, $all, $regex, $size, $maxDistance
+ *
+ * Can be used on Numbers or Dates.
+ *
+ * Thing.where('type').$nin(array)
+ */
+
+'gt gte lt lte ne in nin all regex size maxDistance'.split(' ').forEach( function ($conditional) {
+ Query.prototype['$' + $conditional] =
+ Query.prototype[$conditional] = function (path, val) {
+ if (arguments.length === 1) {
+ val = path;
+ path = this._currPath
+ }
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds['$' + $conditional] = val;
+ return this;
+ };
+});
+
+/**
+ * notEqualTo
+ *
+ * alias of `query.$ne()`
+ */
+
+Query.prototype.notEqualTo = Query.prototype.ne;
+
+/**
+ * $mod, $near
+ */
+
+;['mod', 'near'].forEach( function ($conditional) {
+ Query.prototype['$' + $conditional] =
+ Query.prototype[$conditional] = function (path, val) {
+ if (arguments.length === 1) {
+ val = path;
+ path = this._currPath
+ } else if (arguments.length === 2 && !Array.isArray(val)) {
+ val = utils.args(arguments);
+ path = this._currPath;
+ } else if (arguments.length === 3) {
+ val = utils.args(arguments, 1);
+ }
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds['$' + $conditional] = val;
+ return this;
+ };
+});
+
+/**
+ * $exists
+ */
+
+Query.prototype.$exists =
+Query.prototype.exists = function (path, val) {
+ if (arguments.length === 0) {
+ path = this._currPath
+ val = true;
+ } else if (arguments.length === 1) {
+ if ('boolean' === typeof path) {
+ val = path;
+ path = this._currPath;
+ } else {
+ val = true;
+ }
+ }
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds['$exists'] = val;
+ return this;
+};
+
+/**
+ * $elemMatch
+ */
+
+Query.prototype.$elemMatch =
+Query.prototype.elemMatch = function (path, criteria) {
+ var block;
+ if ('Object' === path.constructor.name) {
+ criteria = path;
+ path = this._currPath;
+ } else if ('function' === typeof path) {
+ block = path;
+ path = this._currPath;
+ } else if ('Object' === criteria.constructor.name) {
+ } else if ('function' === typeof criteria) {
+ block = criteria;
+ } else {
+ throw new Error("Argument error");
+ }
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ if (block) {
+ criteria = new Query();
+ block(criteria);
+ conds['$elemMatch'] = criteria._conditions;
+ } else {
+ conds['$elemMatch'] = criteria;
+ }
+ return this;
+};
+
+/**
+ * @private
+ */
+
+function me () { return this }
+
+/**
+ * Spatial queries
+ */
+
+// query.within.box()
+// query.within.center()
+var within = 'within $within'.split(' ');
+within.push('wherein', '$wherein'); // deprecated, an old mistake possibly?
+within.forEach(function (getter) {
+ Object.defineProperty(Query.prototype, getter, {
+ get: me
+ });
+});
+
+Query.prototype['$box'] =
+Query.prototype.box = function (path, val) {
+ if (arguments.length === 1) {
+ val = path;
+ path = this._currPath;
+ }
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds['$within'] = { '$box': [val.ll, val.ur] };
+ return this;
+};
+
+Query.prototype['$center'] =
+Query.prototype.center = function (path, val) {
+ if (arguments.length === 1) {
+ val = path;
+ path = this._currPath;
+ }
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds['$within'] = { '$center': [val.center, val.radius] };
+ return this;
+};
+
+Query.prototype['$centerSphere'] =
+Query.prototype.centerSphere = function (path, val) {
+ if (arguments.length === 1) {
+ val = path;
+ path = this._currPath;
+ }
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds['$within'] = { '$centerSphere': [val.center, val.radius] };
+ return this;
+};
+
+/**
+ * select
+ *
+ * _also aliased as fields()_
+ *
+ * Chainable method for specifying which fields
+ * to include or exclude from the document that is
+ * returned from MongoDB.
+ *
+ * Examples:
+ * query.fields({a: 1, b: 1, c: 1, _id: 0});
+ * query.fields('a b c');
+ *
+ * @param {Object}
+ */
+
+Query.prototype.select =
+Query.prototype.fields = function () {
+ var arg0 = arguments[0];
+ if (!arg0) return this;
+ if ('Object' === arg0.constructor.name || Array.isArray(arg0)) {
+ this._applyFields(arg0);
+ } else if (arguments.length === 1 && typeof arg0 === 'string') {
+ this._applyFields({only: arg0});
+ } else {
+ this._applyFields({only: this._parseOnlyExcludeFields.apply(this, arguments)});
+ }
+ return this;
+};
+
+/**
+ * only
+ *
+ * Chainable method for adding the specified fields to the
+ * object of fields to only include.
+ *
+ * Examples:
+ * query.only('a b c');
+ * query.only('a', 'b', 'c');
+ * query.only(['a', 'b', 'c']);
+ *
+ * @param {String|Array} space separated list of fields OR
+ * an array of field names
+ * We can also take arguments as the "array" of field names
+ * @api public
+ */
+
+Query.prototype.only = function (fields) {
+ fields = this._parseOnlyExcludeFields.apply(this, arguments);
+ this._applyFields({ only: fields });
+ return this;
+};
+
+/**
+ * exclude
+ *
+ * Chainable method for adding the specified fields to the
+ * object of fields to exclude.
+ *
+ * Examples:
+ * query.exclude('a b c');
+ * query.exclude('a', 'b', 'c');
+ * query.exclude(['a', 'b', 'c']);
+ *
+ * @param {String|Array} space separated list of fields OR
+ * an array of field names
+ * We can also take arguments as the "array" of field names
+ * @api public
+ */
+
+Query.prototype.exclude = function (fields) {
+ fields = this._parseOnlyExcludeFields.apply(this, arguments);
+ this._applyFields({ exclude: fields });
+ return this;
+};
+
+/**
+ * $slice()
+ */
+
+Query.prototype['$slice'] =
+Query.prototype.slice = function (path, val) {
+ if (arguments.length === 1) {
+ val = path;
+ path = this._currPath
+ } else if (arguments.length === 2) {
+ if ('number' === typeof path) {
+ val = [path, val];
+ path = this._currPath;
+ }
+ } else if (arguments.length === 3) {
+ val = utils.args(arguments, 1);
+ }
+ var myFields = this._fields || (this._fields = {});
+ myFields[path] = { '$slice': val };
+ return this;
+};
+
+/**
+ * Private method for interpreting the different ways
+ * you can pass in fields to both Query.prototype.only
+ * and Query.prototype.exclude.
+ *
+ * @param {String|Array|Object} fields
+ * @api private
+ */
+
+Query.prototype._parseOnlyExcludeFields = function (fields) {
+ if (1 === arguments.length && 'string' === typeof fields) {
+ fields = fields.split(' ');
+ } else if (Array.isArray(fields)) {
+ // do nothing
+ } else {
+ fields = utils.args(arguments);
+ }
+ return fields;
+};
+
+/**
+ * Private method for interpreting and applying the different
+ * ways you can specify which fields you want to include
+ * or exclude.
+ *
+ * Example 1: Include fields 'a', 'b', and 'c' via an Array
+ * query.fields('a', 'b', 'c');
+ * query.fields(['a', 'b', 'c']);
+ *
+ * Example 2: Include fields via 'only' shortcut
+ * query.only('a b c');
+ *
+ * Example 3: Exclude fields via 'exclude' shortcut
+ * query.exclude('a b c');
+ *
+ * Example 4: Include fields via MongoDB's native format
+ * query.fields({a: 1, b: 1, c: 1})
+ *
+ * Example 5: Exclude fields via MongoDB's native format
+ * query.fields({a: 0, b: 0, c: 0});
+ *
+ * @param {Object|Array} the formatted collection of fields to
+ * include and/or exclude
+ * @api private
+ */
+
+Query.prototype._applyFields = function (fields) {
+ var $fields
+ , pathList;
+
+ if (Array.isArray(fields)) {
+ $fields = fields.reduce(function ($fields, field) {
+ $fields[field] = 1;
+ return $fields;
+ }, {});
+ } else if (pathList = fields.only || fields.exclude) {
+ $fields =
+ this._parseOnlyExcludeFields(pathList)
+ .reduce(function ($fields, field) {
+ $fields[field] = fields.only ? 1: 0;
+ return $fields;
+ }, {});
+ } else if ('Object' === fields.constructor.name) {
+ $fields = fields;
+ } else {
+ throw new Error("fields is invalid");
+ }
+
+ var myFields = this._fields || (this._fields = {});
+ for (var k in $fields) myFields[k] = $fields[k];
+};
+
+/**
+ * sort
+ *
+ * Sets the sort
+ *
+ * Examples:
+ * query.sort('test', 1)
+ * query.sort('field', -1)
+ * query.sort('field', -1, 'test', 1)
+ *
+ * @api public
+ */
+
+Query.prototype.sort = function () {
+ var sort = this.options.sort || (this.options.sort = []);
+
+ inGroupsOf(2, arguments, function (field, value) {
+ sort.push([field, value]);
+ });
+
+ return this;
+};
+
+/**
+ * asc
+ *
+ * Sorts ascending.
+ *
+ * query.asc('name', 'age');
+ */
+
+Query.prototype.asc = function () {
+ var sort = this.options.sort || (this.options.sort = []);
+ for (var i = 0, l = arguments.length; i < l; i++) {
+ sort.push([arguments[i], 1]);
+ }
+ return this;
+};
+
+/**
+ * desc
+ *
+ * Sorts descending.
+ *
+ * query.desc('name', 'age');
+ */
+
+Query.prototype.desc = function () {
+ var sort = this.options.sort || (this.options.sort = []);
+ for (var i = 0, l = arguments.length; i < l; i++) {
+ sort.push([arguments[i], -1]);
+ }
+ return this;
+};
+
+/**
+ * limit, skip, maxscan, snapshot, batchSize, comment
+ *
+ * Sets these associated options.
+ *
+ * query.comment('feed query');
+ */
+
+;['limit', 'skip', 'maxscan', 'snapshot', 'batchSize', 'comment'].forEach( function (method) {
+ Query.prototype[method] = function (v) {
+ this.options[method] = v;
+ return this;
+ };
+});
+
+/**
+ * hint
+ *
+ * Sets query hints.
+ *
+ * Examples:
+ * new Query().hint({ indexA: 1, indexB: -1})
+ * new Query().hint("indexA", 1, "indexB", -1)
+ *
+ * @param {Object|String} v
+ * @param {Int} [multi]
+ * @return {Query}
+ * @api public
+ */
+
+Query.prototype.hint = function (v, multi) {
+ var hint = this.options.hint || (this.options.hint = {})
+ , k
+
+ if (multi) {
+ inGroupsOf(2, arguments, function (field, val) {
+ hint[field] = val;
+ });
+ } else if ('Object' === v.constructor.name) {
+ // must keep object keys in order so don't use Object.keys()
+ for (k in v) {
+ hint[k] = v[k];
+ }
+ }
+
+ return this;
+};
+
+/**
+ * slaveOk
+ *
+ * Sets slaveOk option.
+ *
+ * new Query().slaveOk() <== true
+ * new Query().slaveOk(true)
+ * new Query().slaveOk(false)
+ *
+ * @param {Boolean} v (defaults to true)
+ * @api public
+ */
+
+Query.prototype.slaveOk = function (v) {
+ this.options.slaveOk = arguments.length ? !!v : true;
+ return this;
+};
+
+/**
+ * tailable
+ *
+ * Sets tailable option.
+ *
+ * new Query().tailable() <== true
+ * new Query().tailable(true)
+ * new Query().tailable(false)
+ *
+ * @param {Boolean} v (defaults to true)
+ * @api public
+ */
+
+Query.prototype.tailable = function (v) {
+ this.options.tailable = arguments.length ? !!v : true;
+ return this;
+};
+
+/**
+ * execFind
+ *
+ * @api private
+ */
+
+Query.prototype.execFind = function (callback) {
+ var model = this.model
+ , promise = new Promise(callback);
+
+ try {
+ this.cast(model);
+ } catch (err) {
+ return promise.error(err);
+ }
+
+ // apply default schematype path selections
+ this._applyPaths();
+
+ var self = this
+ , castQuery = this._conditions
+ , options = this._optionsForExec(model)
+
+ var fields = utils.clone(options.fields = this._fields);
+
+ model.collection.find(castQuery, options, function (err, cursor) {
+ if (err) return promise.error(err);
+ cursor.toArray(tick(cb));
+ });
+
+ function cb (err, docs) {
+ if (err) return promise.error(err);
+
+ var arr = []
+ , count = docs.length;
+
+ if (!count) return promise.complete([]);
+
+ for (var i = 0, l = docs.length; i < l; i++) {
+ arr[i] = new model(undefined, fields);
+
+ // skip _id for pre-init hooks
+ delete arr[i]._doc._id;
+
+ arr[i].init(docs[i], self, function (err) {
+ if (err) return promise.error(err);
+ --count || promise.complete(arr);
+ });
+ }
+ }
+
+ return this;
+};
+
+/**
+ * each()
+ *
+ * Streaming cursors.
+ *
+ * The `callback` is called repeatedly for each document
+ * found in the collection as it's streamed. If an error
+ * occurs streaming stops.
+ *
+ * Example:
+ * query.each(function (err, user) {
+ * if (err) return res.end("aww, received an error. all done.");
+ * if (user) {
+ * res.write(user.name + '\n')
+ * } else {
+ * res.end("reached end of cursor. all done.");
+ * }
+ * });
+ *
+ * A third parameter may also be used in the callback which
+ * allows you to iterate the cursor manually.
+ *
+ * Example:
+ * query.each(function (err, user, next) {
+ * if (err) return res.end("aww, received an error. all done.");
+ * if (user) {
+ * res.write(user.name + '\n')
+ * doSomethingAsync(next);
+ * } else {
+ * res.end("reached end of cursor. all done.");
+ * }
+ * });
+ *
+ * @param {Function} callback
+ * @return {Query}
+ * @api public
+ */
+
+Query.prototype.each = function (callback) {
+ var model = this.model
+ , options = this._optionsForExec(model)
+ , manual = 3 == callback.length
+ , self = this
+
+ try {
+ this.cast(model);
+ } catch (err) {
+ return callback(err);
+ }
+
+ var fields = utils.clone(options.fields = this._fields);
+
+ function complete (err, val) {
+ if (complete.ran) return;
+ complete.ran = true;
+ callback(err, val);
+ }
+
+ model.collection.find(this._conditions, options, function (err, cursor) {
+ if (err) return complete(err);
+
+ var ticks = 0;
+ next();
+
+ function next () {
+ // nextTick is necessary to avoid stack overflows when
+ // dealing with large result sets. yield occasionally.
+ if (!(++ticks % 20)) {
+ process.nextTick(function () {
+ cursor.nextObject(onNextObject);
+ });
+ } else {
+ cursor.nextObject(onNextObject);
+ }
+ }
+
+ function onNextObject (err, doc) {
+ if (err) return complete(err);
+
+ // when doc is null we hit the end of the cursor
+ if (!doc) return complete(null, null);
+
+ var instance = new model(undefined, fields);
+
+ // skip _id for pre-init hooks
+ delete instance._doc._id;
+
+ instance.init(doc, self, function (err) {
+ if (err) return complete(err);
+
+ if (manual) {
+ callback(null, instance, next);
+ } else {
+ callback(null, instance);
+ next();
+ }
+ });
+ }
+
+ });
+
+ return this;
+}
+
+/**
+ * findOne
+ *
+ * Casts the query, sends the findOne command to mongodb.
+ * Upon receiving the document, we initialize a mongoose
+ * document based on the returned document from mongodb,
+ * and then we invoke a callback on our mongoose document.
+ *
+ * @param {Function} callback function (err, found)
+ * @api public
+ */
+
+Query.prototype.findOne = function (callback) {
+ this.op = 'findOne';
+
+ if (!callback) return this;
+
+ var model = this.model;
+ var promise = new Promise(callback);
+
+ try {
+ this.cast(model);
+ } catch (err) {
+ promise.error(err);
+ return this;
+ }
+
+ // apply default schematype path selections
+ this._applyPaths();
+
+ var self = this
+ , castQuery = this._conditions
+ , options = this._optionsForExec(model)
+
+ var fields = utils.clone(options.fields = this._fields);
+
+ model.collection.findOne(castQuery, options, tick(function (err, doc) {
+ if (err) return promise.error(err);
+ if (!doc) return promise.complete(null);
+
+ var casted = new model(undefined, fields);
+
+ // skip _id for pre-init hooks
+ delete casted._doc._id;
+
+ casted.init(doc, self, function (err) {
+ if (err) return promise.error(err);
+ promise.complete(casted);
+ });
+ }));
+
+ return this;
+};
+
+/**
+ * count
+ *
+ * Casts this._conditions and sends a count
+ * command to mongodb. Invokes a callback upon
+ * receiving results
+ *
+ * @param {Function} callback fn(err, cardinality)
+ * @api public
+ */
+
+Query.prototype.count = function (callback) {
+ this.op = 'count';
+ var model = this.model;
+
+ try {
+ this.cast(model);
+ } catch (err) {
+ return callback(err);
+ }
+
+ var castQuery = this._conditions;
+ model.collection.count(castQuery, tick(callback));
+
+ return this;
+};
+
+/**
+ * distinct
+ *
+ * Casts this._conditions and sends a distinct
+ * command to mongodb. Invokes a callback upon
+ * receiving results
+ *
+ * @param {Function} callback fn(err, cardinality)
+ * @api public
+ */
+
+Query.prototype.distinct = function (field, callback) {
+ this.op = 'distinct';
+ var model = this.model;
+
+ try {
+ this.cast(model);
+ } catch (err) {
+ return callback(err);
+ }
+
+ var castQuery = this._conditions;
+ model.collection.distinct(field, castQuery, tick(callback));
+
+ return this;
+};
+
+/**
+ * These operators require casting docs
+ * to real Documents for Update operations.
+ * @private
+ */
+
+var castOps = {
+ $push: 1
+ , $pushAll: 1
+ , $addToSet: 1
+ , $set: 1
+};
+
+/**
+ * These operators should be cast to numbers instead
+ * of their path schema type.
+ * @private
+ */
+
+var numberOps = {
+ $pop: 1
+ , $unset: 1
+ , $inc: 1
+}
+
+/**
+ * update
+ *
+ * Casts the `doc` according to the model Schema and
+ * sends an update command to MongoDB.
+ *
+ * _All paths passed that are not $atomic operations
+ * will become $set ops so we retain backwards compatibility._
+ *
+ * Example:
+ * `Model.update({..}, { title: 'remove words' }, ...)`
+ *
+ * becomes
+ *
+ * `Model.update({..}, { $set: { title: 'remove words' }}, ...)`
+ *
+ *
+ * _Passing an empty object `{}` as the doc will result
+ * in a no-op. The update operation will be ignored and the
+ * callback executed without sending the command to MongoDB so as
+ * to prevent accidently overwritting the collection._
+ *
+ * @param {Object} doc - the update
+ * @param {Function} callback - fn(err)
+ * @api public
+ */
+
+Query.prototype.update = function update (doc, callback) {
+ this.op = 'update';
+ this._updateArg = doc;
+
+ var model = this.model
+ , options = this._optionsForExec(model)
+ , useSet = model.options['use$SetOnSave']
+ , castQuery
+ , castDoc
+
+ try {
+ this.cast(model);
+ castQuery = this._conditions;
+ } catch (err) {
+ return callback(err);
+ }
+
+ try {
+ castDoc = this._castUpdate(doc);
+ } catch (err) {
+ return callback(err);
+ }
+
+ if (castDoc) {
+ model.collection.update(castQuery, castDoc, options, tick(callback));
+ } else {
+ process.nextTick(function () {
+ callback(null);
+ });
+ }
+
+ return this;
+};
+
+/**
+ * Casts obj for an update command.
+ *
+ * @param {Object} obj
+ * @return {Object} obj after casting its values
+ * @api private
+ */
+
+Query.prototype._castUpdate = function _castUpdate (obj) {
+ var ops = Object.keys(obj)
+ , i = ops.length
+ , ret = {}
+ , hasKeys
+ , val
+
+ while (i--) {
+ var op = ops[i];
+ hasKeys = true;
+ if ('$' !== op[0]) {
+ // fix up $set sugar
+ if (!ret.$set) {
+ if (obj.$set) {
+ ret.$set = obj.$set;
+ } else {
+ ret.$set = {};
+ }
+ }
+ ret.$set[op] = obj[op];
+ ops.splice(i, 1);
+ if (!~ops.indexOf('$set')) ops.push('$set');
+ } else if ('$set' === op) {
+ if (!ret.$set) {
+ ret[op] = obj[op];
+ }
+ } else {
+ ret[op] = obj[op];
+ }
+ }
+
+ // cast each value
+ i = ops.length;
+
+ while (i--) {
+ op = ops[i];
+ val = ret[op];
+ if ('Object' === val.constructor.name) {
+ this._walkUpdatePath(val, op);
+ } else {
+ var msg = 'Invalid atomic update value for ' + op + '. '
+ + 'Expected an object, received ' + typeof val;
+ throw new Error(msg);
+ }
+ }
+
+ return hasKeys && ret;
+}
+
+/**
+ * Walk each path of obj and cast its values
+ * according to its schema.
+ *
+ * @param {Object} obj - part of a query
+ * @param {String} op - the atomic operator ($pull, $set, etc)
+ * @param {String} pref - path prefix (internal only)
+ * @private
+ */
+
+Query.prototype._walkUpdatePath = function _walkUpdatePath (obj, op, pref) {
+ var strict = this.model.schema.options.strict
+ , prefix = pref ? pref + '.' : ''
+ , keys = Object.keys(obj)
+ , i = keys.length
+ , schema
+ , key
+ , val
+
+ while (i--) {
+ key = keys[i];
+ val = obj[key];
+
+ if (val && 'Object' === val.constructor.name) {
+ // watch for embedded doc schemas
+ schema = this._getSchema(prefix + key);
+ if (schema && schema.caster && op in castOps) {
+ // embedded doc schema
+
+ if (strict && !schema) {
+ // path is not in our strict schema. do not include
+ delete obj[key];
+ } else {
+ if ('$each' in val) {
+ obj[key] = {
+ $each: this._castUpdateVal(schema, val.$each, op)
+ }
+ } else {
+ obj[key] = this._castUpdateVal(schema, val, op);
+ }
+ }
+ } else {
+ this._walkUpdatePath(val, op, prefix + key);
+ }
+ } else {
+ schema = '$each' === key
+ ? this._getSchema(pref)
+ : this._getSchema(prefix + key);
+
+ if (strict && !schema) {
+ delete obj[key];
+ } else {
+ obj[key] = this._castUpdateVal(schema, val, op, key);
+ }
+ }
+ }
+}
+
+/**
+ * Casts `val` according to `schema` and atomic `op`.
+ *
+ * @param {Schema} schema
+ * @param {Object} val
+ * @param {String} op - the atomic operator ($pull, $set, etc)
+ * @param {String} [$conditional]
+ * @private
+ */
+
+Query.prototype._castUpdateVal = function _castUpdateVal (schema, val, op, $conditional) {
+ if (!schema) {
+ // non-existing schema path
+ return op in numberOps
+ ? Number(val)
+ : val
+ }
+
+ if (schema.caster && op in castOps &&
+ ('Object' === val.constructor.name || Array.isArray(val))) {
+ // Cast values for ops that add data to MongoDB.
+ // Ensures embedded documents get ObjectIds etc.
+ var tmp = schema.cast(val);
+
+ if (Array.isArray(val)) {
+ val = tmp;
+ } else {
+ val = tmp[0];
+ }
+ }
+
+ if (op in numberOps) return Number(val);
+ if (/^\$/.test($conditional)) return schema.castForQuery($conditional, val);
+ return schema.castForQuery(val)
+}
+
+/**
+ * Finds the schema for `path`. This is different than
+ * calling `schema.path` as it also resolves paths with
+ * positional selectors (something.$.another.$.path).
+ *
+ * @param {String} path
+ * @private
+ */
+
+Query.prototype._getSchema = function _getSchema (path) {
+ var schema = this.model.schema
+ , pathschema = schema.path(path);
+
+ if (pathschema)
+ return pathschema;
+
+ // look for arrays
+ return (function search (parts, schema) {
+ var p = parts.length + 1
+ , foundschema
+ , trypath
+
+ while (p--) {
+ trypath = parts.slice(0, p).join('.');
+ foundschema = schema.path(trypath);
+ if (foundschema) {
+ if (foundschema.caster) {
+ // Now that we found the array, we need to check if there
+ // are remaining document paths to look up for casting.
+ // Also we need to handle array.$.path since schema.path
+ // doesn't work for that.
+ if (p !== parts.length) {
+ if ('$' === parts[p]) {
+ // comments.$.comments.$.title
+ return search(parts.slice(p+1), foundschema.schema);
+ } else {
+ // this is the last path of the selector
+ return search(parts.slice(p), foundschema.schema);
+ }
+ }
+ }
+ return foundschema;
+ }
+ }
+ })(path.split('.'), schema)
+}
+
+/**
+ * remove
+ *
+ * Casts the query, sends the remove command to
+ * mongodb where the query contents, and then
+ * invokes a callback upon receiving the command
+ * result.
+ *
+ * @param {Function} callback
+ * @api public
+ */
+
+Query.prototype.remove = function (callback) {
+ this.op = 'remove';
+
+ var model = this.model
+ , options = this._optionsForExec(model);
+
+ try {
+ this.cast(model);
+ } catch (err) {
+ return callback(err);
+ }
+
+ var castQuery = this._conditions;
+ model.collection.remove(castQuery, options, tick(callback));
+ return this;
+};
+
+/**
+ * populate
+ *
+ * Sets population options.
+ * @api public
+ */
+
+Query.prototype.populate = function (path, fields, conditions, options) {
+ // The order of fields/conditions args is opposite Model.find but
+ // necessary to keep backward compatibility (fields could be
+ // an array, string, or object literal).
+ this.options.populate[path] =
+ new PopulateOptions(fields, conditions, options);
+
+ return this;
+};
+
+/**
+ * Populate options constructor
+ * @private
+ */
+
+function PopulateOptions (fields, conditions, options) {
+ this.conditions = conditions;
+ this.fields = fields;
+ this.options = options;
+}
+
+// make it compatible with utils.clone
+PopulateOptions.prototype.constructor = Object;
+
+/**
+ * stream
+ *
+ * Returns a stream interface
+ *
+ * Example:
+ * Thing.find({ name: /^hello/ }).stream().pipe(res)
+ *
+ * @api public
+ */
+
+Query.prototype.stream = function stream () {
+ return new QueryStream(this);
+}
+
+/**
+ * @private
+ * @TODO
+ */
+
+Query.prototype.explain = function () {
+ throw new Error("Unimplemented");
+};
+
+// TODO Add being able to skip casting -- e.g., this would be nice for scenarios like
+// if you're migrating to usernames from user id numbers:
+// query.where('user_id').in([4444, 'brian']);
+// TODO "immortal" cursors - (only work on capped collections)
+// TODO geoNear command
+
+/**
+ * Exports.
+ */
+
+module.exports = Query;
+module.exports.QueryStream = QueryStream;
diff --git a/node_modules/mongoose/lib/querystream.js b/node_modules/mongoose/lib/querystream.js
new file mode 100644
index 0000000..4093e60
--- /dev/null
+++ b/node_modules/mongoose/lib/querystream.js
@@ -0,0 +1,179 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Stream = require('stream').Stream
+var utils = require('./utils')
+
+/**
+ * QueryStream
+ *
+ * Returns a stream interface for the `query`.
+ *
+ * @param {Query} query
+ * @return {Stream}
+ */
+
+function QueryStream (query) {
+ Stream.call(this);
+
+ this.query = query;
+ this.readable = true;
+ this.paused = false;
+ this._cursor = null;
+ this._destroyed = null;
+ this._fields = null;
+ this._ticks = 0;
+
+ // give time to hook up events
+ var self = this;
+ process.nextTick(function () {
+ self._init();
+ });
+}
+
+/**
+ * Inherit from Stream
+ * @private
+ */
+
+QueryStream.prototype.__proto__ = Stream.prototype;
+
+/**
+ * Flag stating whether or not this stream is readable.
+ */
+
+QueryStream.prototype.readable;
+
+/**
+ * Flag stating whether or not this stream is paused.
+ */
+
+QueryStream.prototype.paused;
+
+/**
+ * Initialize the query.
+ * @private
+ */
+
+QueryStream.prototype._init = function () {
+ if (this._destroyed) return;
+
+ var query = this.query
+ , model = query.model
+ , options = query._optionsForExec(model)
+ , self = this
+
+ try {
+ query.cast(model);
+ } catch (err) {
+ return self.destroy(err);
+ }
+
+ self._fields = utils.clone(options.fields = query._fields);
+
+ model.collection.find(query._conditions, options, function (err, cursor) {
+ if (err) return self.destroy(err);
+ self._cursor = cursor;
+ self._next();
+ });
+}
+
+/**
+ * Pull the next document from the cursor.
+ * @private
+ */
+
+QueryStream.prototype._next = function () {
+ if (this.paused || this._destroyed) return;
+
+ var self = this;
+
+ // nextTick is necessary to avoid stack overflows when
+ // dealing with large result sets. yield occasionally.
+ if (!(++this._ticks % 20)) {
+ process.nextTick(function () {
+ self._cursor.nextObject(function (err, doc) {
+ self._onNextObject(err, doc);
+ });
+ });
+ } else {
+ self._cursor.nextObject(function (err, doc) {
+ self._onNextObject(err, doc);
+ });
+ }
+}
+
+/**
+ * Handle each document as its returned from the cursor
+ * transforming the raw `doc` from -native into a model
+ * instance.
+ *
+ * @private
+ */
+
+QueryStream.prototype._onNextObject = function (err, doc) {
+ if (err) return this.destroy(err);
+
+ // when doc is null we hit the end of the cursor
+ if (!doc) {
+ return this.destroy();
+ }
+
+ var instance = new this.query.model(undefined, this._fields);
+
+ // skip _id for pre-init hooks
+ delete instance._doc._id;
+
+ var self = this;
+ instance.init(doc, this.query, function (err) {
+ if (err) return self.destroy(err);
+ self.emit('data', instance);
+ self._next();
+ });
+}
+
+/**
+ * Pauses this stream.
+ */
+
+QueryStream.prototype.pause = function () {
+ this.paused = true;
+}
+
+/**
+ * Resumes this stream.
+ */
+
+QueryStream.prototype.resume = function () {
+ this.paused = false;
+ this._next();
+}
+
+/**
+ * Destroys the stream, closing the underlying
+ * cursor. No more events will be emitted.
+ */
+
+QueryStream.prototype.destroy = function (err) {
+ if (this._destroyed) return;
+ this._destroyed = true;
+ this.readable = false;
+
+ if (this._cursor) {
+ this._cursor.close();
+ }
+
+ if (err) {
+ this.emit('error', err);
+ }
+
+ this.emit('close');
+}
+
+// TODO - maybe implement the -native raw option to pass binary?
+//QueryStream.prototype.setEncoding = function () {
+//}
+
+module.exports = exports = QueryStream;
diff --git a/node_modules/mongoose/lib/schema.js b/node_modules/mongoose/lib/schema.js
new file mode 100644
index 0000000..1dda865
--- /dev/null
+++ b/node_modules/mongoose/lib/schema.js
@@ -0,0 +1,565 @@
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = require('events').EventEmitter
+ , VirtualType = require('./virtualtype')
+ , utils = require('./utils')
+ , NamedScope
+ , Query
+ , Types
+
+/**
+ * Schema constructor.
+ *
+ * @param {Object} definition
+ * @api public
+ */
+
+function Schema (obj, options) {
+ this.paths = {};
+ this.virtuals = {};
+ this.inherits = {};
+ this.callQueue = [];
+ this._indexes = [];
+ this.methods = {};
+ this.statics = {};
+ this.tree = {};
+
+ // set options
+ this.options = utils.options({
+ safe: true
+ , 'use$SetOnSave': true
+ , strict: false
+ }, options);
+
+ // build paths
+ if (obj)
+ this.add(obj);
+
+ if (!this.paths['_id'] && !this.options.noId) {
+ this.add({ _id: {type: ObjectId, auto: true} });
+ }
+
+ if (!this.paths['id'] && !this.options.noVirtualId) {
+ this.virtual('id').get(function () {
+ if (this.__id) {
+ return this.__id;
+ }
+
+ return this.__id = null == this._id
+ ? null
+ : this._id.toString();
+ });
+ }
+
+ delete this.options.noVirtualId;
+};
+
+/**
+ * Inherit from EventEmitter.
+ */
+
+Schema.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Schema by paths
+ *
+ * Example (embedded doc):
+ * {
+ * 'test' : SchemaType,
+ * , 'test.test' : SchemaType,
+ * , 'first_name' : SchemaType
+ * }
+ *
+ * @api private
+ */
+
+Schema.prototype.paths;
+
+/**
+ * Schema as a tree
+ *
+ * Example:
+ * {
+ * '_id' : ObjectId
+ * , 'nested' : {
+ * 'key': String
+ * }
+ * }
+ *
+ * @api private
+ */
+
+Schema.prototype.tree;
+
+/**
+ * Sets the keys
+ *
+ * @param {Object} keys
+ * @param {String} prefix
+ * @api public
+ */
+
+Schema.prototype.add = function add (obj, prefix) {
+ prefix = prefix || '';
+ for (var i in obj) {
+ if (null == obj[i]) {
+ throw new TypeError('Invalid value for schema path `'+ prefix + i +'`');
+ }
+
+ if (obj[i].constructor.name == 'Object' && (!obj[i].type || obj[i].type.type)) {
+ if (Object.keys(obj[i]).length)
+ this.add(obj[i], prefix + i + '.');
+ else
+ this.path(prefix + i, obj[i]); // mixed type
+ } else
+ this.path(prefix + i, obj[i]);
+ }
+};
+
+/**
+ * Sets a path (if arity 2)
+ * Gets a path (if arity 1)
+ *
+ * @param {String} path
+ * @param {Object} constructor
+ * @api public
+ */
+
+Schema.prototype.path = function (path, obj) {
+ if (obj == undefined) {
+ if (this.paths[path]) return this.paths[path];
+
+ // Sometimes path will come in as
+ // pathNameA.4.pathNameB where 4 means the index
+ // of an embedded document in an embedded array.
+ // In this case, we need to jump to the Array's
+ // schema and call path() from there to resolve to
+ // the correct path type
+
+ var last
+ , self = this
+ , subpaths = path.split(/\.(\d+)\.?/)
+ .filter(Boolean) // removes empty strings
+
+ if (subpaths.length > 1) {
+ last = subpaths.length - 1;
+ return subpaths.reduce(function (val, subpath, i) {
+ if (val && !val.schema) {
+ if (i === last && !/\D/.test(subpath) && val instanceof Types.Array) {
+ return val.caster; // StringSchema, NumberSchema, etc
+ } else {
+ return val;
+ }
+ }
+
+ if (!/\D/.test(subpath)) { // 'path.0.subpath' on path 0
+ return val;
+ }
+
+ return val ? val.schema.path(subpath)
+ : self.path(subpath);
+ }, null);
+ }
+
+ return this.paths[subpaths[0]];
+ }
+
+ // update the tree
+ var subpaths = path.split(/\./)
+ , last = subpaths.pop()
+ , branch = this.tree;
+
+ subpaths.forEach(function(path) {
+ if (!branch[path]) branch[path] = {};
+ branch = branch[path];
+ });
+
+ branch[last] = utils.clone(obj);
+
+ this.paths[path] = Schema.interpretAsType(path, obj);
+ return this;
+};
+
+/**
+ * Converts -- e.g., Number, [SomeSchema],
+ * { type: String, enum: ['m', 'f'] } -- into
+ * the appropriate Mongoose Type, which we use
+ * later for casting, validation, etc.
+ * @param {String} path
+ * @param {Object} constructor
+ */
+
+Schema.interpretAsType = function (path, obj) {
+ if (obj.constructor.name != 'Object')
+ obj = { type: obj };
+
+ // Get the type making sure to allow keys named "type"
+ // and default to mixed if not specified.
+ // { type: { type: String, default: 'freshcut' } }
+ var type = obj.type && !obj.type.type
+ ? obj.type
+ : {};
+
+ if (type.constructor.name == 'Object') {
+ return new Types.Mixed(path, obj);
+ }
+
+ if (Array.isArray(type) || type == Array) {
+ // if it was specified through { type } look for `cast`
+ var cast = type == Array
+ ? obj.cast
+ : type[0];
+
+ if (cast instanceof Schema) {
+ return new Types.DocumentArray(path, cast, obj);
+ }
+
+ return new Types.Array(path, cast || Types.Mixed, obj);
+ }
+
+ if (undefined == Types[type.name]) {
+ throw new TypeError('Undefined type at `' + path +
+ '`\n Did you try nesting Schemas? ' +
+ 'You can only nest using refs or arrays.');
+ }
+
+ return new Types[type.name](path, obj);
+};
+
+/**
+ * Iterates through the schema's paths, passing the path string and type object
+ * to the callback.
+ *
+ * @param {Function} callback function - fn(pathstring, type)
+ * @return {Schema} this for chaining
+ * @api public
+ */
+
+Schema.prototype.eachPath = function (fn) {
+ var keys = Object.keys(this.paths)
+ , len = keys.length;
+
+ for (var i = 0; i < len; ++i) {
+ fn(keys[i], this.paths[keys[i]]);
+ }
+
+ return this;
+};
+
+/**
+ * Returns an Array of path strings that are required.
+ * @api public
+ */
+
+Object.defineProperty(Schema.prototype, 'requiredPaths', {
+ get: function () {
+ var paths = this.paths
+ , pathnames = Object.keys(paths)
+ , i = pathnames.length
+ , pathname, path
+ , requiredPaths = [];
+ while (i--) {
+ pathname = pathnames[i];
+ path = paths[pathname];
+ if (path.isRequired) requiredPaths.push(pathname);
+ }
+ return requiredPaths;
+ }
+});
+
+/**
+ * Given a path, returns whether it is a real, virtual, or
+ * ad-hoc/undefined path
+ *
+ * @param {String} path
+ * @return {String}
+ * @api public
+ */
+Schema.prototype.pathType = function (path) {
+ if (path in this.paths) return 'real';
+ if (path in this.virtuals) return 'virtual';
+ return 'adhocOrUndefined';
+};
+
+/**
+ * Adds a method call to the queue
+ *
+ * @param {String} method name
+ * @param {Array} arguments
+ * @api private
+ */
+
+Schema.prototype.queue = function(name, args){
+ this.callQueue.push([name, args]);
+ return this;
+};
+
+/**
+ * Defines a pre for the document
+ *
+ * @param {String} method
+ * @param {Function} callback
+ * @api public
+ */
+
+Schema.prototype.pre = function(){
+ return this.queue('pre', arguments);
+};
+
+/**
+ * Defines a post for the document
+ *
+ * @param {String} method
+ * @param {Function} callback
+ * @api public
+ */
+
+Schema.prototype.post = function(method, fn){
+ return this.queue('on', arguments);
+};
+
+/**
+ * Registers a plugin for this schema
+ *
+ * @param {Function} plugin callback
+ * @api public
+ */
+
+Schema.prototype.plugin = function (fn, opts) {
+ fn(this, opts);
+ return this;
+};
+
+/**
+ * Adds a method
+ *
+ * @param {String} method name
+ * @param {Function} handler
+ * @api public
+ */
+
+Schema.prototype.method = function (name, fn) {
+ if ('string' != typeof name)
+ for (var i in name)
+ this.methods[i] = name[i];
+ else
+ this.methods[name] = fn;
+ return this;
+};
+
+/**
+ * Defines a static method
+ *
+ * @param {String} name
+ * @param {Function} handler
+ * @api public
+ */
+
+Schema.prototype.static = function(name, fn) {
+ if ('string' != typeof name)
+ for (var i in name)
+ this.statics[i] = name[i];
+ else
+ this.statics[name] = fn;
+ return this;
+};
+
+/**
+ * Defines an index (most likely compound)
+ * Example:
+ * schema.index({ first: 1, last: -1 })
+ *
+ * @param {Object} field
+ * @param {Object} optional options object
+ * @api public
+ */
+
+Schema.prototype.index = function (fields, options) {
+ this._indexes.push([fields, options || {}]);
+ return this;
+};
+
+/**
+ * Sets/gets an option
+ *
+ * @param {String} key
+ * @param {Object} optional value
+ * @api public
+ */
+
+Schema.prototype.set = function (key, value) {
+ if (arguments.length == 1)
+ return this.options[key];
+ this.options[key] = value;
+ return this;
+};
+
+/**
+ * Compiles indexes from fields and schema-level indexes
+ *
+ * @api public
+ */
+
+Schema.prototype.__defineGetter__('indexes', function () {
+ var indexes = []
+ , seenSchemas = [];
+
+ collectIndexes(this);
+
+ return indexes;
+
+ function collectIndexes (schema, prefix) {
+ if (~seenSchemas.indexOf(schema)) return;
+ seenSchemas.push(schema);
+
+ var index;
+ var paths = schema.paths;
+ prefix = prefix || '';
+
+ for (var i in paths) {
+ if (paths[i]) {
+ if (paths[i] instanceof Types.DocumentArray) {
+ collectIndexes(paths[i].schema, i + '.');
+ } else {
+ index = paths[i]._index;
+
+ if (index !== false && index !== null){
+ var field = {};
+ field[prefix + i] = '2d' === index ? index : 1;
+ indexes.push([field, 'Object' === index.constructor.name ? index : {} ]);
+ }
+ }
+ }
+ }
+
+ if (prefix) {
+ fixSubIndexPaths(schema, prefix);
+ } else {
+ indexes = indexes.concat(schema._indexes);
+ }
+ }
+
+ /**
+ * Checks for indexes added to subdocs using Schema.index().
+ * These indexes need their paths prefixed properly.
+ *
+ * schema._indexes = [ [indexObj, options], [indexObj, options] ..]
+ */
+
+ function fixSubIndexPaths (schema, prefix) {
+ var subindexes = schema._indexes
+ , len = subindexes.length
+ , indexObj
+ , newindex
+ , klen
+ , keys
+ , key
+ , i = 0
+ , j
+
+ for (i = 0; i < len; ++i) {
+ indexObj = subindexes[i][0];
+ keys = Object.keys(indexObj);
+ klen = keys.length;
+ newindex = {};
+
+ // use forward iteration, order matters
+ for (j = 0; j < klen; ++j) {
+ key = keys[j];
+ newindex[prefix + key] = indexObj[key];
+ }
+
+ indexes.push([newindex, subindexes[i][1]]);
+ }
+ }
+
+});
+
+/**
+ * Retrieves or creates the virtual type with the given name.
+ *
+ * @param {String} name
+ * @return {VirtualType}
+ */
+
+Schema.prototype.virtual = function (name, options) {
+ var virtuals = this.virtuals || (this.virtuals = {});
+ var parts = name.split('.');
+ return virtuals[name] = parts.reduce(function (mem, part, i) {
+ mem[part] || (mem[part] = (i === parts.length-1)
+ ? new VirtualType(options)
+ : {});
+ return mem[part];
+ }, this.tree);
+};
+
+/**
+ * Fetches the virtual type with the given name.
+ * Should be distinct from virtual because virtual auto-defines a new VirtualType
+ * if the path doesn't exist.
+ *
+ * @param {String} name
+ * @return {VirtualType}
+ */
+
+Schema.prototype.virtualpath = function (name) {
+ return this.virtuals[name];
+};
+
+Schema.prototype.namedScope = function (name, fn) {
+ var namedScopes = this.namedScopes || (this.namedScopes = new NamedScope)
+ , newScope = Object.create(namedScopes)
+ , allScopes = namedScopes.scopesByName || (namedScopes.scopesByName = {});
+ allScopes[name] = newScope;
+ newScope.name = name;
+ newScope.block = fn;
+ newScope.query = new Query();
+ newScope.decorate(namedScopes, {
+ block0: function (block) {
+ return function () {
+ block.call(this.query);
+ return this;
+ };
+ },
+ blockN: function (block) {
+ return function () {
+ block.apply(this.query, arguments);
+ return this;
+ };
+ },
+ basic: function (query) {
+ return function () {
+ this.query.find(query);
+ return this;
+ };
+ }
+ });
+ return newScope;
+};
+
+/**
+ * ObjectId schema identifier. Not an actual ObjectId, only used for Schemas.
+ *
+ * @api public
+ */
+
+function ObjectId () {
+ throw new Error('This is an abstract interface. Its only purpose is to mark '
+ + 'fields as ObjectId in the schema creation.');
+}
+
+/**
+ * Module exports.
+ */
+
+module.exports = exports = Schema;
+
+// require down here because of reference issues
+exports.Types = Types = require('./schema/index');
+NamedScope = require('./namedscope')
+Query = require('./query');
+
+exports.ObjectId = ObjectId;
+
diff --git a/node_modules/mongoose/lib/schema/array.js b/node_modules/mongoose/lib/schema/array.js
new file mode 100644
index 0000000..6458a73
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/array.js
@@ -0,0 +1,232 @@
+/**
+ * Module dependencies.
+ */
+
+var SchemaType = require('../schematype')
+ , CastError = SchemaType.CastError
+ , NumberSchema = require('./number')
+ , Types = {
+ Boolean: require('./boolean')
+ , Date: require('./date')
+ , Number: ArrayNumberSchema
+ , String: require('./string')
+ , ObjectId: require('./objectid')
+ , Buffer: require('./buffer')
+ }
+ , MongooseArray = require('../types').Array
+ , Mixed = require('./mixed')
+ , Query = require('../query')
+ , isMongooseObject = require('../utils').isMongooseObject
+
+/**
+ * Array SchemaType constructor
+ *
+ * @param {String} key
+ * @param {SchemaType} cast
+ * @api private
+ */
+
+function SchemaArray (key, cast, options) {
+ SchemaType.call(this, key, options);
+
+ if (cast) {
+ var castOptions = {};
+
+ if ('Object' === cast.constructor.name) {
+ if (cast.type) {
+ // support { type: Woot }
+ castOptions = cast;
+ cast = cast.type;
+ delete castOptions.type;
+ } else {
+ cast = Mixed;
+ }
+ }
+
+ var caster = cast.name in Types ? Types[cast.name] : cast;
+ this.casterConstructor = caster;
+ this.caster = new caster(null, castOptions);
+ }
+
+ var self = this
+ , defaultArr
+ , fn;
+
+ if (this.defaultValue) {
+ defaultArr = this.defaultValue;
+ fn = 'function' == typeof defaultArr;
+ }
+
+ this.default(function(){
+ var arr = fn ? defaultArr() : defaultArr || [];
+ return new MongooseArray(arr, self.path, this);
+ });
+};
+
+/**
+ * Inherits from SchemaType.
+ */
+
+SchemaArray.prototype.__proto__ = SchemaType.prototype;
+
+/**
+ * Check required
+ *
+ * @api private
+ */
+
+SchemaArray.prototype.checkRequired = function (value) {
+ return !!(value && value.length);
+};
+
+/**
+ * Overrides the getters application for the population special-case
+ * TODO: implement this in SchemaObjectIdArray
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @api private
+ */
+
+SchemaArray.prototype.applyGetters = function (value, scope) {
+ if (this.caster.options && this.caster.options.ref) {
+ // means the object id was populated
+ return value;
+ }
+
+ return SchemaType.prototype.applyGetters.call(this, value, scope);
+};
+
+/**
+ * Casts contents
+ *
+ * @param {Object} value
+ * @param {Document} document that triggers the casting
+ * @param {Boolean} whether this is an initialization cast
+ * @api private
+ */
+
+SchemaArray.prototype.cast = function (value, doc, init) {
+ if (Array.isArray(value)) {
+ if (!(value instanceof MongooseArray)) {
+ value = new MongooseArray(value, this.path, doc);
+ }
+
+ if (this.caster) {
+ try {
+ for (var i = 0, l = value.length; i < l; i++) {
+ value[i] = this.caster.cast(value[i], doc, init);
+ }
+ } catch (e) {
+ // rethrow
+ throw new CastError(e.type, value);
+ }
+ }
+
+ return value;
+ } else {
+ return this.cast([value], doc, init);
+ }
+};
+
+SchemaArray.prototype.castForQuery = function ($conditional, val) {
+ var handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler)
+ throw new Error("Can't use " + $conditional + " with Array.");
+ val = handler.call(this, val);
+ } else {
+ val = $conditional;
+ var proto = this.casterConstructor.prototype;
+ var method = proto.castForQuery || proto.cast;
+ if (Array.isArray(val)) {
+ val = val.map(function (v) {
+ if (method) v = method.call(proto, v);
+ return isMongooseObject(v)
+ ? v.toObject()
+ : v;
+ });
+ } else if (method) {
+ val = method.call(proto, val);
+ }
+ }
+ return val && isMongooseObject(val)
+ ? val.toObject()
+ : val;
+};
+
+SchemaArray.prototype.$conditionalHandlers = {
+ '$all': function handle$all (val) {
+ if (!Array.isArray(val)) {
+ val = [val];
+ }
+
+ val = val.map(function (v) {
+ if (v && 'Object' === v.constructor.name) {
+ var o = {};
+ o[this.path] = v;
+ var query = new Query(o);
+ query.cast(this.casterConstructor);
+ return query._conditions[this.path];
+ }
+ return v;
+ }, this);
+
+ return this.castForQuery(val);
+ }
+ , '$elemMatch': function (val) {
+ var query = new Query(val);
+ query.cast(this.casterConstructor);
+ return query._conditions;
+ }
+ , '$size': function (val) {
+ return ArrayNumberSchema.prototype.cast.call(this, val);
+ }
+ , '$ne': SchemaArray.prototype.castForQuery
+ , '$in': SchemaArray.prototype.castForQuery
+ , '$nin': SchemaArray.prototype.castForQuery
+ , '$regex': SchemaArray.prototype.castForQuery
+ , '$near': SchemaArray.prototype.castForQuery
+ , '$nearSphere': SchemaArray.prototype.castForQuery
+ , '$within': function(val) {
+ var query = new Query(val);
+ query.cast(this.casterConstructor)
+ return query._conditions;
+ }
+ , '$maxDistance': function (val) {
+ return ArrayNumberSchema.prototype.cast.call(this, val);
+ }
+};
+
+/**
+ * Number casting for arrays (equivalent, but without MongoseNumber)
+ *
+ * @see GH-176
+ * @param {Object} value
+ * @api private
+ */
+
+// subclass number schema to override casting
+// to disallow non-numbers being saved
+function ArrayNumberSchema (key, options) {
+ NumberSchema.call(this, key, options);
+}
+
+ArrayNumberSchema.prototype.__proto__ = NumberSchema.prototype;
+
+ArrayNumberSchema.prototype.cast = function (value) {
+ if (!isNaN(value)) {
+ if (value instanceof Number || typeof value == 'number' ||
+ (value.toString && value.toString() == Number(value)))
+ return Number(value);
+ }
+
+ throw new CastError('number', value);
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = SchemaArray;
diff --git a/node_modules/mongoose/lib/schema/boolean.js b/node_modules/mongoose/lib/schema/boolean.js
new file mode 100644
index 0000000..574fdd8
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/boolean.js
@@ -0,0 +1,59 @@
+
+/**
+ * Module dependencies.
+ */
+
+var SchemaType = require('../schematype');
+
+/**
+ * Boolean SchemaType constructor.
+ *
+ * @param {String} path
+ * @param {Object} options
+ * @api private
+ */
+
+function SchemaBoolean (path, options) {
+ SchemaType.call(this, path, options);
+};
+
+/**
+ * Inherits from SchemaType.
+ */
+SchemaBoolean.prototype.__proto__ = SchemaType.prototype;
+
+/**
+ * Required validator for date
+ *
+ * @api private
+ */
+
+SchemaBoolean.prototype.checkRequired = function (value) {
+ return value === true || value === false;
+};
+
+/**
+ * Casts to boolean
+ *
+ * @param {Object} value to cast
+ * @api private
+ */
+
+SchemaBoolean.prototype.cast = function (value) {
+ if (value === null) return value;
+ if (value === '0') return false;
+ return !!value;
+};
+
+SchemaBoolean.prototype.castForQuery = function ($conditional, val) {
+ if (arguments.length === 1) {
+ val = $conditional;
+ }
+ return this.cast(val);
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = SchemaBoolean;
diff --git a/node_modules/mongoose/lib/schema/buffer.js b/node_modules/mongoose/lib/schema/buffer.js
new file mode 100644
index 0000000..64e12a2
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/buffer.js
@@ -0,0 +1,106 @@
+/**
+ * Module dependencies.
+ */
+
+var SchemaType = require('../schematype')
+ , CastError = SchemaType.CastError
+ , BufferNumberSchema = function () {}
+ , MongooseBuffer = require('../types').Buffer
+ , Binary = MongooseBuffer.Binary
+ , Query = require('../query');
+
+/**
+ * Buffer SchemaType constructor
+ *
+ * @param {String} key
+ * @param {SchemaType} cast
+ * @api private
+ */
+
+function SchemaBuffer (key, options) {
+ SchemaType.call(this, key, options, 'Buffer');
+};
+
+/**
+ * Inherits from SchemaType.
+ */
+
+SchemaBuffer.prototype.__proto__ = SchemaType.prototype;
+
+/**
+ * Check required
+ *
+ * @api private
+ */
+
+SchemaBuffer.prototype.checkRequired = function (value) {
+ return !!(value && value.length);
+};
+
+/**
+ * Casts contents
+ *
+ * @param {Object} value
+ * @param {Document} document that triggers the casting
+ * @api private
+ */
+
+SchemaBuffer.prototype.cast = function (value, doc, init) {
+ if (SchemaType._isRef(this, value, init)) return value;
+
+ if (Buffer.isBuffer(value)) {
+ if (!(value instanceof MongooseBuffer)) {
+ value = new MongooseBuffer(value, [this.path, doc]);
+ }
+
+ return value;
+ } else if (value instanceof Binary) {
+ return new MongooseBuffer(value.value(true), [this.path, doc]);
+ }
+
+ if ('string' === typeof value || Array.isArray(value)) {
+ return new MongooseBuffer(value, [this.path, doc]);
+ }
+
+ throw new CastError('buffer', value);
+};
+
+function handleSingle (val) {
+ return this.castForQuery(val);
+}
+
+function handleArray (val) {
+ var self = this;
+ return val.map( function (m) {
+ return self.castForQuery(m);
+ });
+}
+
+SchemaBuffer.prototype.$conditionalHandlers = {
+ '$ne' : handleSingle
+ , '$in' : handleArray
+ , '$nin': handleArray
+ , '$gt' : handleSingle
+ , '$lt' : handleSingle
+ , '$gte': handleSingle
+ , '$lte': handleSingle
+};
+
+SchemaBuffer.prototype.castForQuery = function ($conditional, val) {
+ var handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler)
+ throw new Error("Can't use " + $conditional + " with Buffer.");
+ return handler.call(this, val);
+ } else {
+ val = $conditional;
+ return this.cast(val).toObject();
+ }
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = SchemaBuffer;
diff --git a/node_modules/mongoose/lib/schema/date.js b/node_modules/mongoose/lib/schema/date.js
new file mode 100644
index 0000000..3a7bda8
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/date.js
@@ -0,0 +1,116 @@
+
+/**
+ * Module requirements.
+ */
+
+var SchemaType = require('../schematype')
+ , CastError = SchemaType.CastError;
+
+/**
+ * Date SchemaType constructor.
+ *
+ * @param {String} key
+ * @param {Object} options
+ * @api private
+ */
+
+function SchemaDate (key, options) {
+ SchemaType.call(this, key, options);
+};
+
+/**
+ * Inherits from SchemaType.
+ */
+
+SchemaDate.prototype.__proto__ = SchemaType.prototype;
+
+/**
+ * Required validator for date
+ *
+ * @api private
+ */
+
+SchemaDate.prototype.checkRequired = function (value) {
+ return value instanceof Date;
+};
+
+/**
+ * Casts to date
+ *
+ * @param {Object} value to cast
+ * @api private
+ */
+
+SchemaDate.prototype.cast = function (value) {
+ if (value === null || value === '')
+ return null;
+
+ if (value instanceof Date)
+ return value;
+
+ var date;
+
+ // support for timestamps
+ if (value instanceof Number || 'number' == typeof value
+ || String(value) == Number(value))
+ date = new Date(Number(value));
+
+ // support for date strings
+ else if (value.toString)
+ date = new Date(value.toString());
+
+ if (date.toString() != 'Invalid Date')
+ return date;
+
+ throw new CastError('date', value);
+};
+
+/**
+ * Date Query casting.
+ *
+ * @api private
+ */
+
+function handleSingle (val) {
+ return this.cast(val);
+}
+
+function handleArray (val) {
+ var self = this;
+ return val.map( function (m) {
+ return self.cast(m);
+ });
+}
+
+SchemaDate.prototype.$conditionalHandlers = {
+ '$lt': handleSingle
+ , '$lte': handleSingle
+ , '$gt': handleSingle
+ , '$gte': handleSingle
+ , '$ne': handleSingle
+ , '$in': handleArray
+ , '$nin': handleArray
+ , '$all': handleArray
+};
+
+SchemaDate.prototype.castForQuery = function ($conditional, val) {
+ var handler;
+
+ if (2 !== arguments.length) {
+ return this.cast($conditional);
+ }
+
+ handler = this.$conditionalHandlers[$conditional];
+
+ if (!handler) {
+ throw new Error("Can't use " + $conditional + " with Date.");
+ }
+
+ return handler.call(this, val);
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = SchemaDate;
diff --git a/node_modules/mongoose/lib/schema/documentarray.js b/node_modules/mongoose/lib/schema/documentarray.js
new file mode 100644
index 0000000..38276ff
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/documentarray.js
@@ -0,0 +1,141 @@
+
+/**
+ * Module dependencies.
+ */
+
+var SchemaType = require('../schematype')
+ , ArrayType = require('./array')
+ , MongooseDocumentArray = require('../types/documentarray')
+ , Subdocument = require('../types/embedded')
+ , CastError = SchemaType.CastError
+ , Document = require('../document');
+
+/**
+ * SubdocsArray SchemaType constructor
+ *
+ * @param {String} key
+ * @param {Schema} schema
+ * @param {Object} options
+ * @api private
+ */
+
+function DocumentArray (key, schema, options) {
+ // compile an embedded document for this schema
+ // TODO Move this into parent model compilation for performance improvement?
+ function EmbeddedDocument () {
+ Subdocument.apply(this, arguments);
+ };
+
+ EmbeddedDocument.prototype.__proto__ = Subdocument.prototype;
+ EmbeddedDocument.prototype.schema = schema;
+ EmbeddedDocument.schema = schema;
+
+ // apply methods
+ for (var i in schema.methods) {
+ EmbeddedDocument.prototype[i] = schema.methods[i];
+ }
+
+ // apply statics
+ for (var i in schema.statics)
+ EmbeddedDocument[i] = schema.statics[i];
+
+ ArrayType.call(this, key, EmbeddedDocument, options);
+
+ this.caster = EmbeddedDocument;
+ this.caster.options = options;
+
+ var self = this;
+
+ this.schema = schema;
+ this.default(function(){
+ return new MongooseDocumentArray([], self.path, this);
+ });
+};
+
+/**
+ * Inherits from ArrayType.
+ */
+
+DocumentArray.prototype.__proto__ = ArrayType.prototype;
+
+/**
+ * Performs local validations first, then validations on each embedded doc
+ *
+ * @api private
+ */
+
+DocumentArray.prototype.doValidate = function (array, fn, scope) {
+ var self = this;
+ SchemaType.prototype.doValidate.call(this, array, function(err){
+ if (err) return fn(err);
+
+ var count = array && array.length
+ , error = false;
+
+ if (!count) return fn();
+
+ array.forEach(function(doc, index){
+ doc.validate(function(err){
+ if (err && !error){
+ // rewrite they key
+ err.key = self.key + '.' + index + '.' + err.key;
+ fn(err);
+ error = true;
+ } else {
+ --count || fn();
+ }
+ });
+ });
+ }, scope);
+};
+
+/**
+ * Casts contents
+ *
+ * @param {Object} value
+ * @param {Document} document that triggers the casting
+ * @api private
+ */
+
+DocumentArray.prototype.cast = function (value, doc, init, prev) {
+ var subdoc
+ , i
+
+ if (Array.isArray(value)) {
+ if (!(value instanceof MongooseDocumentArray)) {
+ value = new MongooseDocumentArray(value, this.path, doc);
+ }
+
+ i = value.length;
+
+ while (i--) {
+ if (!(value[i] instanceof Subdocument)) {
+ if (init) {
+ subdoc = new this.caster(null, value);
+ // skip _id for pre-init hooks
+ delete subdoc._doc._id;
+ value[i] = subdoc.init(value[i]);
+ } else {
+ subdoc = prev && prev.id(value[i]._id) ||
+ new this.caster(null, value);
+ subdoc.set(value[i]);
+ // if set() is hooked it will have no return value
+ // see gh-746
+ value[i] = subdoc;
+ }
+ }
+ }
+
+ return value;
+ } else {
+ return this.cast([value], doc, init, prev);
+ }
+
+ throw new CastError('documentarray', value);
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = DocumentArray;
diff --git a/node_modules/mongoose/lib/schema/index.js b/node_modules/mongoose/lib/schema/index.js
new file mode 100644
index 0000000..ac424c3
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/index.js
@@ -0,0 +1,22 @@
+
+/**
+ * Module exports.
+ */
+
+exports.String = require('./string');
+
+exports.Number = require('./number');
+
+exports.Boolean = require('./boolean');
+
+exports.DocumentArray = require('./documentarray');
+
+exports.Array = require('./array');
+
+exports.Buffer = require('./buffer');
+
+exports.Date = require('./date');
+
+exports.ObjectId = require('./objectid');
+
+exports.Mixed = require('./mixed');
diff --git a/node_modules/mongoose/lib/schema/mixed.js b/node_modules/mongoose/lib/schema/mixed.js
new file mode 100644
index 0000000..6f98be0
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/mixed.js
@@ -0,0 +1,63 @@
+
+/**
+ * Module dependencies.
+ */
+
+var SchemaType = require('../schematype');
+
+/**
+ * Mixed SchemaType constructor.
+ *
+ * @param {String} path
+ * @param {Object} options
+ * @api private
+ */
+
+function Mixed (path, options) {
+ // make sure empty array defaults are handled
+ if (options &&
+ options.default &&
+ Array.isArray(options.default) &&
+ 0 === options.default.length) {
+ options.default = Array;
+ }
+
+ SchemaType.call(this, path, options);
+};
+
+/**
+ * Inherits from SchemaType.
+ */
+Mixed.prototype.__proto__ = SchemaType.prototype;
+
+/**
+ * Required validator for mixed type
+ *
+ * @api private
+ */
+
+Mixed.prototype.checkRequired = function (val) {
+ return true;
+};
+
+/**
+ * Noop casting
+ *
+ * @param {Object} value to cast
+ * @api private
+ */
+
+Mixed.prototype.cast = function (val) {
+ return val;
+};
+
+Mixed.prototype.castForQuery = function ($cond, val) {
+ if (arguments.length === 2) return val;
+ return $cond;
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = Mixed;
diff --git a/node_modules/mongoose/lib/schema/number.js b/node_modules/mongoose/lib/schema/number.js
new file mode 100644
index 0000000..75a14f6
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/number.js
@@ -0,0 +1,142 @@
+/**
+ * Module requirements.
+ */
+
+var SchemaType = require('../schematype')
+ , CastError = SchemaType.CastError
+ , MongooseNumber = require('../types/number');
+
+/**
+ * Number SchemaType constructor.
+ *
+ * @param {String} key
+ * @param {Object} options
+ * @api private
+ */
+
+function SchemaNumber (key, options) {
+ SchemaType.call(this, key, options, 'Number');
+};
+
+/**
+ * Inherits from SchemaType.
+ */
+
+SchemaNumber.prototype.__proto__ = SchemaType.prototype;
+
+/**
+ * Required validator for number
+ *
+ * @api private
+ */
+
+SchemaNumber.prototype.checkRequired = function checkRequired (value) {
+ if (SchemaType._isRef(this, value, true)) {
+ return null != value;
+ } else {
+ return typeof value == 'number' || value instanceof Number;
+ }
+};
+
+/**
+ * Sets a maximum number validator
+ *
+ * @param {Number} minimum number
+ * @api public
+ */
+
+SchemaNumber.prototype.min = function (value, message) {
+ if (this.minValidator)
+ this.validators = this.validators.filter(function(v){
+ return v[1] != 'min';
+ });
+ if (value != null)
+ this.validators.push([function(v){
+ return v === null || v >= value;
+ }, 'min']);
+ return this;
+};
+
+/**
+ * Sets a maximum number validator
+ *
+ * @param {Number} maximum number
+ * @api public
+ */
+
+SchemaNumber.prototype.max = function (value, message) {
+ if (this.maxValidator)
+ this.validators = this.validators.filter(function(v){
+ return v[1] != 'max';
+ });
+ if (value != null)
+ this.validators.push([this.maxValidator = function(v){
+ return v === null || v <= value;
+ }, 'max']);
+ return this;
+};
+
+/**
+ * Casts to number
+ *
+ * @param {Object} value to cast
+ * @param {Document} document that triggers the casting
+ * @api private
+ */
+
+SchemaNumber.prototype.cast = function (value, doc, init) {
+ if (SchemaType._isRef(this, value, init)) return value;
+
+ if (!isNaN(value)){
+ if (null === value) return value;
+ if ('' === value) return null;
+ if ('string' === typeof value) value = Number(value);
+ if (value instanceof Number || typeof value == 'number' ||
+ (value.toString && value.toString() == Number(value)))
+ return new MongooseNumber(value, this.path, doc);
+ }
+
+ throw new CastError('number', value);
+};
+
+function handleSingle (val) {
+ return this.cast(val).valueOf();
+}
+
+function handleArray (val) {
+ var self = this;
+ return val.map( function (m) {
+ return self.cast(m).valueOf();
+ });
+}
+
+SchemaNumber.prototype.$conditionalHandlers = {
+ '$lt' : handleSingle
+ , '$lte': handleSingle
+ , '$gt' : handleSingle
+ , '$gte': handleSingle
+ , '$ne' : handleSingle
+ , '$in' : handleArray
+ , '$nin': handleArray
+ , '$mod': handleArray
+ , '$all': handleArray
+};
+
+SchemaNumber.prototype.castForQuery = function ($conditional, val) {
+ var handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler)
+ throw new Error("Can't use " + $conditional + " with Number.");
+ return handler.call(this, val);
+ } else {
+ val = this.cast($conditional);
+ return val == null ? val : val.valueOf();
+ }
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = SchemaNumber;
diff --git a/node_modules/mongoose/lib/schema/objectid.js b/node_modules/mongoose/lib/schema/objectid.js
new file mode 100644
index 0000000..a83f95d
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/objectid.js
@@ -0,0 +1,126 @@
+/**
+ * Module dependencies.
+ */
+
+var SchemaType = require('../schematype')
+ , CastError = SchemaType.CastError
+ , driver = global.MONGOOSE_DRIVER_PATH || './../drivers/node-mongodb-native'
+ , oid = require('../types/objectid');
+
+
+/**
+ * ObjectId SchemaType constructor.
+ *
+ * @param {String} key
+ * @param {Object} options
+ * @api private
+ */
+
+function ObjectId (key, options) {
+ SchemaType.call(this, key, options, 'ObjectID');
+};
+
+/**
+ * Inherits from SchemaType.
+ */
+
+ObjectId.prototype.__proto__ = SchemaType.prototype;
+
+/**
+ * Check required
+ *
+ * @api private
+ */
+
+ObjectId.prototype.checkRequired = function checkRequired (value) {
+ if (SchemaType._isRef(this, value, true)) {
+ return null != value;
+ } else {
+ return value instanceof oid;
+ }
+};
+
+/**
+ * Casts to ObjectId
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @param {Boolean} whether this is an initialization cast
+ * @api private
+ */
+
+ObjectId.prototype.cast = function (value, scope, init) {
+ if (SchemaType._isRef(this, value, init)) return value;
+
+ if (value === null) return value;
+
+ if (value instanceof oid)
+ return value;
+
+ if (value._id && value._id instanceof oid)
+ return value._id;
+
+ if (value.toString)
+ return oid.fromString(value.toString());
+
+ throw new CastError('object id', value);
+};
+
+function handleSingle (val) {
+ return this.cast(val);
+}
+
+function handleArray (val) {
+ var self = this;
+ return val.map(function (m) {
+ return self.cast(m);
+ });
+}
+
+ObjectId.prototype.$conditionalHandlers = {
+ '$ne': handleSingle
+ , '$in': handleArray
+ , '$nin': handleArray
+ , '$gt': handleSingle
+ , '$lt': handleSingle
+ , '$gte': handleSingle
+ , '$lte': handleSingle
+ , '$all': handleArray
+};
+
+ObjectId.prototype.castForQuery = function ($conditional, val) {
+ var handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler)
+ throw new Error("Can't use " + $conditional + " with ObjectId.");
+ return handler.call(this, val);
+ } else {
+ val = $conditional;
+ return this.cast(val);
+ }
+};
+
+/**
+ * Adds an auto-generated ObjectId default if turnOn is true.
+ * @param {Boolean} turnOn auto generated ObjectId defaults
+ * @api private
+ */
+
+ObjectId.prototype.auto = function (turnOn) {
+ if (turnOn) {
+ this.default(function(){
+ return new oid();
+ });
+ this.set(function (v) {
+ this.__id = null;
+ return v;
+ })
+ }
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = ObjectId;
diff --git a/node_modules/mongoose/lib/schema/string.js b/node_modules/mongoose/lib/schema/string.js
new file mode 100644
index 0000000..5187e4f
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/string.js
@@ -0,0 +1,180 @@
+
+/**
+ * Module dependencies.
+ */
+
+var SchemaType = require('../schematype')
+ , CastError = SchemaType.CastError;
+
+/**
+ * String SchemaType constructor.
+ *
+ * @param {String} key
+ * @api private
+ */
+
+function SchemaString (key, options) {
+ this.enumValues = [];
+ this.regExp = null;
+ SchemaType.call(this, key, options, 'String');
+};
+
+/**
+ * Inherits from SchemaType.
+ */
+
+SchemaString.prototype.__proto__ = SchemaType.prototype;
+
+/**
+ * Adds enumeration values
+ *
+ * @param {multiple} enumeration values
+ * @api public
+ */
+
+SchemaString.prototype.enum = function () {
+ var len = arguments.length;
+ if (!len || undefined === arguments[0] || false === arguments[0]) {
+ if (this.enumValidator){
+ this.enumValidator = false;
+ this.validators = this.validators.filter(function(v){
+ return v[1] != 'enum';
+ });
+ }
+ return;
+ }
+
+ for (var i = 0; i < len; i++) {
+ if (undefined !== arguments[i]) {
+ this.enumValues.push(this.cast(arguments[i]));
+ }
+ }
+
+ if (!this.enumValidator) {
+ var values = this.enumValues;
+ this.enumValidator = function(v){
+ return ~values.indexOf(v);
+ };
+ this.validators.push([this.enumValidator, 'enum']);
+ }
+};
+
+/**
+ * Adds a lowercase setter
+ *
+ * @api public
+ */
+
+SchemaString.prototype.lowercase = function () {
+ return this.set(function (v) {
+ return v.toLowerCase();
+ });
+};
+
+/**
+ * Adds an uppercase setter
+ *
+ * @api public
+ */
+
+SchemaString.prototype.uppercase = function () {
+ return this.set(function (v) {
+ return v.toUpperCase();
+ });
+};
+
+/**
+ * Adds a trim setter
+ *
+ * @api public
+ */
+
+SchemaString.prototype.trim = function () {
+ return this.set(function (v) {
+ return v.trim();
+ });
+};
+
+/**
+ * Sets a regexp test
+ *
+ * @param {RegExp} regular expression to test against
+ * @param {String} optional validator message
+ * @api public
+ */
+
+SchemaString.prototype.match = function(regExp){
+ this.validators.push([function(v){
+ return regExp.test(v);
+ }, 'regexp']);
+};
+
+/**
+ * Check required
+ *
+ * @api private
+ */
+
+SchemaString.prototype.checkRequired = function checkRequired (value) {
+ if (SchemaType._isRef(this, value, true)) {
+ return null != value;
+ } else {
+ return (value instanceof String || typeof value == 'string') && value.length;
+ }
+};
+
+/**
+ * Casts to String
+ *
+ * @api private
+ */
+
+SchemaString.prototype.cast = function (value, scope, init) {
+ if (SchemaType._isRef(this, value, init)) return value;
+ if (value === null) return value;
+ if ('undefined' !== typeof value && value.toString) return value.toString();
+ throw new CastError('string', value);
+};
+
+function handleSingle (val) {
+ return this.castForQuery(val);
+}
+
+function handleArray (val) {
+ var self = this;
+ return val.map(function (m) {
+ return self.castForQuery(m);
+ });
+}
+
+SchemaString.prototype.$conditionalHandlers = {
+ '$ne' : handleSingle
+ , '$in' : handleArray
+ , '$nin': handleArray
+ , '$gt' : handleSingle
+ , '$lt' : handleSingle
+ , '$gte': handleSingle
+ , '$lte': handleSingle
+ , '$all': handleArray
+ , '$regex': handleSingle
+};
+
+SchemaString.prototype.castForQuery = function ($conditional, val) {
+ var handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler)
+ throw new Error("Can't use " + $conditional + " with String.");
+ return handler.call(this, val);
+ } else {
+ val = $conditional;
+ if (val instanceof RegExp) return val;
+ return this.cast(val);
+ }
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = SchemaString;
diff --git a/node_modules/mongoose/lib/schemadefault.js b/node_modules/mongoose/lib/schemadefault.js
new file mode 100644
index 0000000..a468c85
--- /dev/null
+++ b/node_modules/mongoose/lib/schemadefault.js
@@ -0,0 +1,32 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Schema = require('./schema')
+
+/**
+ * Default model for querying the system.profiles
+ * collection (it only exists when profiling is
+ * enabled.
+ */
+
+exports['system.profile'] = new Schema({
+ ts: Date
+ , info: String // deprecated
+ , millis: Number
+ , op: String
+ , ns: String
+ , query: Schema.Types.Mixed
+ , updateobj: Schema.Types.Mixed
+ , ntoreturn: Number
+ , nreturned: Number
+ , nscanned: Number
+ , responseLength: Number
+ , client: String
+ , user: String
+ , idhack: Boolean
+ , scanAndOrder: Boolean
+ , keyUpdates: Number
+ , cursorid: Number
+}, { noVirtualId: true, noId: true });
diff --git a/node_modules/mongoose/lib/schematype.js b/node_modules/mongoose/lib/schematype.js
new file mode 100644
index 0000000..f2accb6
--- /dev/null
+++ b/node_modules/mongoose/lib/schematype.js
@@ -0,0 +1,428 @@
+/**
+ * Module dependencies.
+ */
+
+var MongooseError = require('./error');
+var utils = require('./utils');
+
+/**
+ * SchemaType constructor
+ *
+ * @param {String} path
+ * @api public
+ */
+
+function SchemaType (path, options, instance) {
+ this.path = path;
+ this.instance = instance;
+ this.validators = [];
+ this.setters = [];
+ this.getters = [];
+ this.options = options;
+ this._index = null;
+ this.selected;
+
+ for (var i in options) if (this[i] && 'function' == typeof this[i]) {
+ var opts = Array.isArray(options[i])
+ ? options[i]
+ : [options[i]];
+
+ this[i].apply(this, opts);
+ }
+};
+
+/**
+ * Base schema. Set by Schema when instantiated.
+ *
+ * @api private
+ */
+
+SchemaType.prototype.base;
+
+/**
+ * Sets a default
+ *
+ * @param {Object} default value
+ * @api public
+ */
+
+SchemaType.prototype.default = function (val) {
+ if (1 === arguments.length) {
+ this.defaultValue = typeof val === 'function'
+ ? val
+ : this.cast(val);
+ return this;
+ } else if (arguments.length > 1) {
+ this.defaultValue = utils.args(arguments);
+ }
+ return this.defaultValue;
+};
+
+/**
+ * Sets index. It can be a boolean or a hash of options
+ * Example:
+ * Schema.path('my.path').index(true);
+ * Schema.path('my.path').index({ unique: true });
+ *
+ * "Direction doesn't matter for single key indexes"
+ * http://www.mongodb.org/display/DOCS/Indexes#Indexes-CompoundKeysIndexes
+ *
+ * @param {Object} true/
+ * @api public
+ */
+
+SchemaType.prototype.index = function (index) {
+ this._index = index;
+ return this;
+};
+
+/**
+ * Adds an unique index
+ *
+ * @param {Boolean}
+ * @api private
+ */
+
+SchemaType.prototype.unique = function (bool) {
+ if (!this._index || 'Object' !== this._index.constructor.name) {
+ this._index = {};
+ }
+
+ this._index.unique = bool;
+ return this;
+};
+
+/**
+ * Adds an unique index
+ *
+ * @param {Boolean}
+ * @api private
+ */
+
+SchemaType.prototype.sparse = function (bool) {
+ if (!this._index || 'Object' !== this._index.constructor.name) {
+ this._index = {};
+ }
+
+ this._index.sparse = bool;
+ return this;
+};
+
+/**
+ * Adds a setter
+ *
+ * @param {Function} setter
+ * @api public
+ */
+
+SchemaType.prototype.set = function (fn) {
+ if ('function' != typeof fn)
+ throw new Error('A setter must be a function.');
+ this.setters.push(fn);
+ return this;
+};
+
+/**
+ * Adds a getter
+ *
+ * @param {Function} getter
+ * @api public
+ */
+
+SchemaType.prototype.get = function (fn) {
+ if ('function' != typeof fn)
+ throw new Error('A getter must be a function.');
+ this.getters.push(fn);
+ return this;
+};
+
+/**
+ * ##validate
+ *
+ * Adds validators.
+ *
+ * Examples:
+ *
+ * function validator () { ... }
+ *
+ * var single = [validator, 'failed']
+ * new Schema({ name: { type: String, validate: single }});
+ *
+ * var many = [
+ * { validator: validator, msg: 'uh oh' }
+ * , { validator: fn, msg: 'failed' }
+ * ]
+ * new Schema({ name: { type: String, validate: many }});
+ *
+ * @param {Object} validator
+ * @param {String} optional error message
+ * @api public
+ */
+
+SchemaType.prototype.validate = function (obj, error) {
+ if ('function' == typeof obj || obj && 'RegExp' === obj.constructor.name) {
+ this.validators.push([obj, error]);
+ return this;
+ }
+
+ var i = arguments.length
+ , arg
+
+ while (i--) {
+ arg = arguments[i];
+ this.validators.push([arg.validator, arg.msg]);
+ }
+
+ return this;
+};
+
+/**
+ * Adds a required validator
+ *
+ * @param {Boolean} enable/disable the validator
+ * @api public
+ */
+
+SchemaType.prototype.required = function (required) {
+ var self = this;
+
+ function __checkRequired (v) {
+ // in here, `this` refers to the validating document.
+ // no validation when this path wasn't selected in the query.
+ if ('isSelected' in this &&
+ !this.isSelected(self.path) &&
+ !this.isModified(self.path)) return true;
+ return self.checkRequired(v);
+ }
+
+ if (false === required) {
+ this.isRequired = false;
+ this.validators = this.validators.filter(function (v) {
+ return v[0].name !== '__checkRequired';
+ });
+ } else {
+ this.isRequired = true;
+ this.validators.push([__checkRequired, 'required']);
+ }
+
+ return this;
+};
+
+/**
+ * Gets the default value
+ *
+ * @param {Object} scope for callback defaults
+ * @api private
+ */
+
+SchemaType.prototype.getDefault = function (scope) {
+ var ret = 'function' === typeof this.defaultValue
+ ? this.defaultValue.call(scope)
+ : this.defaultValue;
+
+ if (null !== ret && undefined !== ret) {
+ return this.cast(ret, scope);
+ } else {
+ return ret;
+ }
+};
+
+/**
+ * Applies setters
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @api private
+ */
+
+SchemaType.prototype.applySetters = function (value, scope, init) {
+ if (SchemaType._isRef(this, value, init)) return value;
+
+ var v = value
+ , setters = this.setters
+ , len = setters.length;
+
+ for (var k = len - 1; k >= 0; k--) {
+ v = setters[k].call(scope, v, this);
+ if (null === v || undefined === v) return v;
+ v = this.cast(v, scope);
+ }
+
+ if (!len) {
+ if (null === v || undefined === v) return v;
+ if (!init) {
+ // if we just initialized we dont recast
+ v = this.cast(v, scope, init);
+ }
+ }
+
+ return v;
+};
+
+/**
+ * Applies getters to a value
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @api private
+ */
+
+SchemaType.prototype.applyGetters = function (value, scope) {
+ if (SchemaType._isRef(this, value, true)) return value;
+
+ var v = value
+ , getters = this.getters
+ , len = getters.length;
+
+ for (var k = len - 1; k >= 0; k--) {
+ v = this.getters[k].call(scope, v, this);
+ if (null === v || undefined === v) return v;
+ v = this.cast(v, scope);
+ }
+
+ if (!len) {
+ if (null === v || undefined === v) return v;
+ v = this.cast(v, scope);
+ }
+
+ return v;
+};
+
+/**
+ * ## select
+ *
+ * Set default select() behavior for this path. True if
+ * this path should always be included in the results,
+ * false if it should be excluded by default. This setting
+ * can be overridden at the query level.
+ *
+ * T = db.model('T', new Schema({ x: { type: String, select: true }}));
+ * T.find(..); // x will always be selected ..
+ * // .. unless overridden;
+ * T.find().select({ x: 0 }).exec();
+ */
+
+SchemaType.prototype.select = function select (val) {
+ this.selected = !! val;
+}
+
+/**
+ * Performs a validation
+ *
+ * @param {Function} callback
+ * @param {Object} scope
+ * @api private
+ */
+
+SchemaType.prototype.doValidate = function (value, fn, scope) {
+ var err = false
+ , path = this.path
+ , count = this.validators.length;
+
+ if (!count) return fn(null);
+
+ function validate (val, msg) {
+ if (err) return;
+ if (val === undefined || val) {
+ --count || fn(null);
+ } else {
+ fn(err = new ValidatorError(path, msg));
+ }
+ }
+
+ this.validators.forEach(function (v) {
+ var validator = v[0]
+ , message = v[1];
+
+ if (validator instanceof RegExp) {
+ validate(validator.test(value), message);
+ } else if ('function' === typeof validator) {
+ if (2 === validator.length) {
+ validator.call(scope, value, function (val) {
+ validate(val, message);
+ });
+ } else {
+ validate(validator.call(scope, value), message);
+ }
+ }
+ });
+};
+
+/**
+ * Determines if value is a valid Reference.
+ *
+ * @param {SchemaType} self
+ * @param {object} value
+ * @param {Boolean} init
+ * @param {MongooseType} instance
+ * @return Boolean
+ * @private
+ */
+
+SchemaType._isRef = function (self, value, init) {
+ if (self.options && self.options.ref && init) {
+ if (null == value) return true;
+ if (value._id && value._id.constructor.name === self.instance) return true;
+ }
+
+ return false;
+}
+
+/**
+ * Schema validator error
+ *
+ * @param {String} path
+ * @param {String} msg
+ * @api private
+ */
+
+function ValidatorError (path, type) {
+ var msg = type
+ ? '"' + type + '" '
+ : '';
+ MongooseError.call(this, 'Validator ' + msg + 'failed for path ' + path);
+ Error.captureStackTrace(this, arguments.callee);
+ this.name = 'ValidatorError';
+ this.path = path;
+ this.type = type;
+};
+
+ValidatorError.prototype.toString = function () {
+ return this.message;
+}
+
+/**
+ * Inherits from MongooseError
+ */
+
+ValidatorError.prototype.__proto__ = MongooseError.prototype;
+
+/**
+ * Cast error
+ *
+ * @api private
+ */
+
+function CastError (type, value) {
+ MongooseError.call(this, 'Cast to ' + type + ' failed for value "' + value + '"');
+ Error.captureStackTrace(this, arguments.callee);
+ this.name = 'CastError';
+ this.type = type;
+ this.value = value;
+};
+
+/**
+ * Inherits from MongooseError.
+ */
+
+CastError.prototype.__proto__ = MongooseError.prototype;
+
+/**
+ * Module exports.
+ */
+
+module.exports = exports = SchemaType;
+
+exports.CastError = CastError;
+
+exports.ValidatorError = ValidatorError;
diff --git a/node_modules/mongoose/lib/statemachine.js b/node_modules/mongoose/lib/statemachine.js
new file mode 100644
index 0000000..fb8ceb5
--- /dev/null
+++ b/node_modules/mongoose/lib/statemachine.js
@@ -0,0 +1,179 @@
+
+/**
+ * Module dependencies.
+ */
+
+var utils = require('./utils');
+
+/**
+ * StateMachine represents a minimal `interface` for the
+ * constructors it builds via StateMachine.ctor(...).
+ *
+ * @api private
+ */
+
+var StateMachine = module.exports = exports = function StateMachine () {
+ this.paths = {};
+ this.states = {};
+}
+
+/**
+ * StateMachine.ctor('state1', 'state2', ...)
+ * A factory method for subclassing StateMachine.
+ * The arguments are a list of states. For each state,
+ * the constructor's prototype gets state transition
+ * methods named after each state. These transition methods
+ * place their path argument into the given state.
+ *
+ * @param {String} state
+ * @param {String} [state]
+ * @return {Function} subclass constructor
+ * @private
+ */
+
+StateMachine.ctor = function () {
+ var states = utils.args(arguments);
+
+ var ctor = function () {
+ StateMachine.apply(this, arguments);
+ this.stateNames = states;
+
+ var i = states.length
+ , state;
+
+ while (i--) {
+ state = states[i];
+ this.states[state] = {};
+ }
+ };
+
+ ctor.prototype.__proto__ = StateMachine.prototype;
+
+ states.forEach(function (state) {
+ // Changes the `path`'s state to `state`.
+ ctor.prototype[state] = function (path) {
+ this._changeState(path, state);
+ }
+ });
+
+ return ctor;
+};
+
+/**
+ * This function is wrapped by the state change functions:
+ * - `require(path)`
+ * - `modify(path)`
+ * - `init(path)`
+ * @api private
+ */
+
+StateMachine.prototype._changeState = function _changeState (path, nextState) {
+ var prevState = this.paths[path]
+ , prevBucket = this.states[prevState];
+
+ delete this.paths[path];
+ if (prevBucket) delete prevBucket[path];
+
+ this.paths[path] = nextState;
+ this.states[nextState][path] = true;
+}
+
+StateMachine.prototype.stateOf = function stateOf (path) {
+ return this.paths[path];
+}
+
+StateMachine.prototype.clear = function clear (state) {
+ var keys = Object.keys(this.states[state])
+ , i = keys.length
+ , path
+
+ while (i--) {
+ path = keys[i];
+ delete this.states[state][path];
+ delete this.paths[path];
+ }
+}
+
+/**
+ * Checks to see if at least one path is in the states passed in via `arguments`
+ * e.g., this.some('required', 'inited')
+ * @param {String} state that we want to check for.
+ * @private
+ */
+
+StateMachine.prototype.some = function some () {
+ var self = this;
+ var what = arguments.length ? arguments : this.stateNames;
+ return Array.prototype.some.call(what, function (state) {
+ return Object.keys(self.states[state]).length;
+ });
+}
+
+/**
+ * This function builds the functions that get assigned to `forEach` and `map`,
+ * since both of those methods share a lot of the same logic.
+ *
+ * @param {String} iterMethod is either 'forEach' or 'map'
+ * @return {Function}
+ * @api private
+ */
+
+StateMachine.prototype._iter = function _iter (iterMethod) {
+ return function () {
+ var numArgs = arguments.length
+ , states = utils.args(arguments, 0, numArgs-1)
+ , callback = arguments[numArgs-1];
+
+ if (!states.length) states = this.stateNames;
+
+ var self = this;
+
+ var paths = states.reduce(function (paths, state) {
+ return paths.concat(Object.keys(self.states[state]));
+ }, []);
+
+ return paths[iterMethod](function (path, i, paths) {
+ return callback(path, i, paths);
+ });
+ };
+}
+
+/**
+ * Iterates over the paths that belong to one of the parameter states.
+ *
+ * The function profile can look like:
+ * this.forEach(state1, fn); // iterates over all paths in state1
+ * this.forEach(state1, state2, fn); // iterates over all paths in state1 or state2
+ * this.forEach(fn); // iterates over all paths in all states
+ *
+ * @param {String} [state]
+ * @param {String} [state]
+ * @param {Function} callback
+ * @private
+ */
+
+StateMachine.prototype.forEach = function forEach () {
+ this.forEach = this._iter('forEach');
+ return this.forEach.apply(this, arguments);
+}
+
+/**
+ * Maps over the paths that belong to one of the parameter states.
+ *
+ * The function profile can look like:
+ * this.forEach(state1, fn); // iterates over all paths in state1
+ * this.forEach(state1, state2, fn); // iterates over all paths in state1 or state2
+ * this.forEach(fn); // iterates over all paths in all states
+ *
+ * @param {String} [state]
+ * @param {String} [state]
+ * @param {Function} callback
+ * @return {Array}
+ * @private
+ */
+
+StateMachine.prototype.map = function map () {
+ this.map = this._iter('map');
+ return this.map.apply(this, arguments);
+}
+
diff --git a/node_modules/mongoose/lib/types/array.js b/node_modules/mongoose/lib/types/array.js
new file mode 100644
index 0000000..67f88b6
--- /dev/null
+++ b/node_modules/mongoose/lib/types/array.js
@@ -0,0 +1,422 @@
+
+/**
+ * Module dependencies.
+ */
+
+var EmbeddedDocument = require('./embedded');
+var Document = require('../document');
+var ObjectId = require('./objectid');
+
+/**
+ * Mongoose Array constructor.
+ * Values always have to be passed to the constructor to initialize, since
+ * otherwise MongooseArray#push will mark the array as modified to the parent.
+ *
+ * @param {Array} values
+ * @param {String} key path
+ * @param {Document} parent document
+ * @api private
+ * @see http://bit.ly/f6CnZU
+ */
+
+function MongooseArray (values, path, doc) {
+ var arr = [];
+ arr.push.apply(arr, values);
+ arr.__proto__ = MongooseArray.prototype;
+
+ arr._atomics = {};
+ arr.validators = [];
+ arr._path = path;
+
+ if (doc) {
+ arr._parent = doc;
+ arr._schema = doc.schema.path(path);
+ }
+
+ return arr;
+};
+
+/**
+ * Inherit from Array
+ */
+
+MongooseArray.prototype = new Array;
+
+/**
+ * Stores a queue of atomic operations to perform
+ *
+ * @api private
+ */
+
+MongooseArray.prototype._atomics;
+
+/**
+ * Parent owner document
+ *
+ * @api private
+ */
+
+MongooseArray.prototype._parent;
+
+/**
+ * Casts a member
+ *
+ * @api private
+ */
+
+MongooseArray.prototype._cast = function (value) {
+ var cast = this._schema.caster.cast
+ , doc = this._parent;
+
+ return cast.call(null, value, doc);
+};
+
+/**
+ * Marks this array as modified.
+ * It is called during a nonAtomicPush, an atomic opteration,
+ * or by an existing embedded document that is modified.
+ *
+ * If it bubbles up from an embedded document change,
+ * then it takes the following arguments (otherwise, takes
+ * 0 arguments)
+ * @param {EmbeddedDocument} embeddedDoc that invokes this method on the Array
+ * @param {String} embeddedPath is what changed inthe embeddedDoc
+ *
+ * @api public
+ */
+
+MongooseArray.prototype._markModified = function (embeddedDoc, embeddedPath) {
+ var parent = this._parent
+ , dirtyPath;
+
+ if (parent) {
+ if (arguments.length) {
+ // If an embedded doc bubbled up the change
+ dirtyPath = [this._path, this.indexOf(embeddedDoc), embeddedPath].join('.');
+ } else {
+ dirtyPath = this._path;
+ }
+ parent.markModified(dirtyPath);
+ }
+
+ return this;
+};
+
+/**
+ * Register an atomic operation with the parent
+ *
+ * @param {Array} operation
+ * @api private
+ */
+
+MongooseArray.prototype._registerAtomic = function (op, val) {
+ var atomics = this._atomics
+ if (op === '$pullAll' || op === '$pushAll' || op === '$addToSet') {
+ atomics[op] || (atomics[op] = []);
+ atomics[op] = atomics[op].concat(val);
+ } else if (op === '$pullDocs') {
+ var pullOp = atomics['$pull'] || (atomics['$pull'] = {})
+ , selector = pullOp['_id'] || (pullOp['_id'] = {'$in' : [] });
+ selector['$in'] = selector['$in'].concat(val);
+ } else {
+ atomics[op] = val;
+ }
+ this._markModified();
+ return this;
+};
+
+/**
+ * Returns true if we have to perform atomics for this, and no normal
+ * operations
+ *
+ * @api public
+ */
+
+MongooseArray.prototype.__defineGetter__('doAtomics', function () {
+ if (!(this._atomics && 'Object' === this._atomics.constructor.name)) {
+ return 0;
+ }
+
+ return Object.keys(this._atomics).length;
+});
+
+/**
+ * Pushes item/s to the array atomically. Overrides Array#push
+ *
+ * @param {Object} value
+ * @api public
+ */
+
+MongooseArray.prototype.$push =
+MongooseArray.prototype.push = function () {
+ var values = [].map.call(arguments, this._cast, this)
+ , ret = [].push.apply(this, values);
+
+ // $pushAll might be fibbed (could be $push). But it makes it easier to
+ // handle what could have been $push, $pushAll combos
+ this._registerAtomic('$pushAll', values);
+ return ret;
+};
+
+/**
+ * Pushes item/s to the array non-atomically
+ *
+ * @param {Object} value
+ * @api public
+ */
+
+MongooseArray.prototype.nonAtomicPush = function () {
+ var values = [].map.call(arguments, this._cast, this)
+ , ret = [].push.apply(this, values);
+ this._markModified();
+ return ret;
+};
+
+/**
+ * Pushes several items at once to the array atomically
+ *
+ * @param {Array} values
+ * @api public
+ */
+
+MongooseArray.prototype.$pushAll = function (value) {
+ var length = this.length;
+ this.nonAtomicPush.apply(this, value);
+ // make sure we access the casted elements
+ this._registerAtomic('$pushAll', this.slice(length));
+ return this;
+};
+
+/**
+ * Pops the array atomically
+ *
+ * @api public
+ */
+
+MongooseArray.prototype.$pop = function () {
+ this._registerAtomic('$pop', 1);
+ return this.pop();
+};
+
+/**
+ * Atomically shifts the array.
+ *
+ * Only works once per doc.save(). Calling this mulitple
+ * times on an array before saving is the same as calling
+ * it once.
+ *
+ * @api public
+ */
+
+MongooseArray.prototype.$shift = function () {
+ this._registerAtomic('$pop', -1);
+ return this.shift();
+};
+
+/**
+ * Removes items from an array atomically
+ *
+ * Examples:
+ * doc.array.remove(ObjectId)
+ * doc.array.remove('tag 1', 'tag 2')
+ *
+ * @param {Object} value to remove
+ * @api public
+ */
+
+MongooseArray.prototype.remove = function () {
+ var args = [].map.call(arguments, this._cast, this);
+ if (args.length == 1)
+ this.$pull(args[0]);
+ else
+ this.$pullAll(args);
+ return args;
+};
+
+/**
+ * Pulls from the array
+ *
+ * @api public
+ */
+
+MongooseArray.prototype.pull =
+MongooseArray.prototype.$pull = function () {
+ var values = [].map.call(arguments, this._cast, this)
+ , oldArr = this._parent.get(this._path)
+ , i = oldArr.length
+ , mem;
+
+ while (i--) {
+ mem = oldArr[i];
+ if (mem instanceof EmbeddedDocument) {
+ if (values.some(function (v) { return v.equals(mem); } )) {
+ oldArr.splice(i, 1);
+ }
+ } else if (~values.indexOf(mem)) {
+ oldArr.splice(i, 1);
+ }
+ }
+
+ if (values[0] instanceof EmbeddedDocument) {
+ this._registerAtomic('$pullDocs', values.map( function (v) { return v._id; } ));
+ } else {
+ this._registerAtomic('$pullAll', values);
+ }
+
+ return this;
+};
+
+/**
+ * Pulls many items from an array
+ *
+ * @api public
+ */
+
+MongooseArray.prototype.$pullAll = function (values) {
+ if (values && values.length) {
+ var oldArr = this._parent.get(this._path)
+ , i = oldArr.length, mem;
+ while (i--) {
+ mem = oldArr[i];
+ if (mem instanceof EmbeddedDocument) {
+ if (values.some( function (v) { return v.equals(mem); } )) {
+ oldArr.splice(i, 1);
+ }
+ } else if (~values.indexOf(mem)) oldArr.splice(i, 1);
+ }
+ if (values[0] instanceof EmbeddedDocument) {
+ this._registerAtomic('$pullDocs', values.map( function (v) { return v._id; } ));
+ } else {
+ this._registerAtomic('$pullAll', values);
+ }
+ }
+ return this;
+};
+
+/**
+ * Splices the array.
+ *
+ * Note: marks the _entire_ array as modified which
+ * will pass the entire thing to $set potentially
+ * overwritting any changes that happen between
+ * when you retrieved the object and when you save
+ * it.
+ *
+ * @api public
+ */
+
+MongooseArray.prototype.splice = function () {
+ var result = [].splice.apply(this, arguments);
+ this._markModified();
+ return result;
+};
+
+/**
+ * Non-atomically unshifts onto the array.
+ *
+ * Note: marks the _entire_ array as modified which
+ * will pass the entire thing to $set potentially
+ * overwritting any changes that happen between
+ * when you retrieved the object and when you save
+ * it.
+ *
+ * @api public
+ */
+
+MongooseArray.prototype.unshift = function () {
+ var values = [].map.call(arguments, this._cast, this);
+ [].unshift.apply(this, values);
+ this._markModified();
+ return this.length;
+};
+
+/**
+ * Adds values to the array if not already present.
+ * @api public
+ */
+
+MongooseArray.prototype.$addToSet =
+MongooseArray.prototype.addToSet = function addToSet () {
+ var values = [].map.call(arguments, this._cast, this)
+ , added = []
+ , type = values[0] instanceof EmbeddedDocument ? 'doc' :
+ values[0] instanceof Date ? 'date' :
+ '';
+
+ values.forEach(function (v) {
+ var found;
+ switch (type) {
+ case 'doc':
+ found = this.some(function(doc){ return doc.equals(v) });
+ break;
+ case 'date':
+ var val = +v;
+ found = this.some(function(d){ return +d === val });
+ break;
+ default:
+ found = ~this.indexOf(v);
+ }
+
+ if (!found) {
+ [].push.call(this, v);
+ this._registerAtomic('$addToSet', v);
+ [].push.call(added, v);
+ }
+ }, this);
+
+ return added;
+};
+
+/**
+ * Returns an Array
+ *
+ * @return {Array}
+ * @api public
+ */
+
+MongooseArray.prototype.toObject = function (options) {
+ if (options && options.depopulate && this[0] instanceof Document) {
+ return this.map(function (doc) {
+ return doc._id;
+ });
+ }
+
+ return this.map(function (doc) {
+ return doc;
+ });
+};
+
+/**
+ * Helper for console.log
+ *
+ * @api public
+ */
+
+MongooseArray.prototype.inspect = function () {
+ return '[' + this.map(function (doc) {
+ return ' ' + doc;
+ }) + ' ]';
+};
+
+/**
+ * Return the index of `obj` or `-1.`
+ *
+ * @param {Object} obj
+ * @return {Number}
+ * @api public
+ */
+
+MongooseArray.prototype.indexOf = function indexOf (obj) {
+ if (obj instanceof ObjectId) obj = obj.toString();
+ for (var i = 0, len = this.length; i < len; ++i) {
+ if (obj == this[i])
+ return i;
+ }
+ return -1;
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = exports = MongooseArray;
diff --git a/node_modules/mongoose/lib/types/buffer.js b/node_modules/mongoose/lib/types/buffer.js
new file mode 100644
index 0000000..9092f20
--- /dev/null
+++ b/node_modules/mongoose/lib/types/buffer.js
@@ -0,0 +1,171 @@
+
+/**
+ * Access driver.
+ */
+
+var driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native';
+
+/**
+ * Module dependencies.
+ */
+
+var Binary = require(driver + '/binary');
+
+/**
+ * Mongoose Buffer constructor.
+ * Values always have to be passed to the constructor to initialize, since
+ * otherwise MongooseBuffer#push will mark the buffer as modified to the parent.
+ *
+ * @param {Buffer} value
+ * @param {String} key path
+ * @param {Document} parent document
+ * @api private
+ * @see http://bit.ly/f6CnZU
+ */
+
+function MongooseBuffer (value, encode, offset) {
+ var length = arguments.length;
+ var val;
+
+ if (0 === length || null === arguments[0] || undefined === arguments[0]) {
+ val = 0;
+ } else {
+ val = value;
+ }
+
+ var encoding;
+ var path;
+ var doc;
+
+ if (Array.isArray(encode)) {
+ // internal casting
+ path = encode[0];
+ doc = encode[1];
+ } else {
+ encoding = encode;
+ }
+
+ var buf = new Buffer(val, encoding, offset);
+ buf.__proto__ = MongooseBuffer.prototype;
+
+ // make sure these internal props don't show up in Object.keys()
+ Object.defineProperties(buf, {
+ validators: { value: [] }
+ , _path: { value: path }
+ , _parent: { value: doc }
+ });
+
+ if (doc && "string" === typeof path) {
+ Object.defineProperty(buf, '_schema', {
+ value: doc.schema.path(path)
+ });
+ }
+
+ return buf;
+};
+
+/**
+ * Inherit from Buffer.
+ */
+
+MongooseBuffer.prototype = new Buffer(0);
+
+/**
+ * Parent owner document
+ *
+ * @api private
+ */
+
+MongooseBuffer.prototype._parent;
+
+
+/**
+* Marks this buffer as modified.
+*
+* @api public
+*/
+
+MongooseBuffer.prototype._markModified = function () {
+ var parent = this._parent;
+
+ if (parent) {
+ parent.markModified(this._path);
+ }
+ return this;
+};
+
+/**
+* Writes the buffer.
+*/
+
+MongooseBuffer.prototype.write = function () {
+ var written = Buffer.prototype.write.apply(this, arguments);
+
+ if (written > 0) {
+ this._markModified();
+ }
+
+ return written;
+};
+
+/**
+* Copy the buffer.
+*
+* Note: Buffer#copy will not mark target as modified so
+* you must copy from a MongooseBuffer for it to work
+* as expected.
+*
+* Work around since copy modifies the target, not this.
+*/
+
+MongooseBuffer.prototype.copy = function (target) {
+ var ret = Buffer.prototype.copy.apply(this, arguments);
+
+ if (target instanceof MongooseBuffer) {
+ target._markModified();
+ }
+
+ return ret;
+};
+
+/**
+ * Compile other Buffer methods marking this buffer as modified.
+ */
+
+;(
+// node < 0.5
+'writeUInt8 writeUInt16 writeUInt32 writeInt8 writeInt16 writeInt32 ' +
+'writeFloat writeDouble fill ' +
+'utf8Write binaryWrite asciiWrite set ' +
+
+// node >= 0.5
+'writeUInt16LE writeUInt16BE writeUInt32LE writeUInt32BE ' +
+'writeInt16LE writeInt16BE writeInt32LE writeInt32BE ' +
+'writeFloatLE writeFloatBE writeDoubleLE writeDoubleBE'
+).split(' ').forEach(function (method) {
+ if (!Buffer.prototype[method]) return;
+ MongooseBuffer.prototype[method] = new Function(
+ 'var ret = Buffer.prototype.'+method+'.apply(this, arguments);' +
+ 'this._markModified();' +
+ 'return ret;'
+ )
+});
+
+/**
+ * Returns a Binary.
+ *
+ * @return {Buffer}
+ * @api public
+ */
+
+MongooseBuffer.prototype.toObject = function () {
+ return new Binary(this, 0x00);
+};
+
+/**
+ * Module exports.
+ */
+
+MongooseBuffer.Binary = Binary;
+
+module.exports = MongooseBuffer;
diff --git a/node_modules/mongoose/lib/types/documentarray.js b/node_modules/mongoose/lib/types/documentarray.js
new file mode 100644
index 0000000..f3c66bb
--- /dev/null
+++ b/node_modules/mongoose/lib/types/documentarray.js
@@ -0,0 +1,133 @@
+
+/**
+ * Module dependencies.
+ */
+
+var MongooseArray = require('./array')
+ , driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native'
+ , ObjectId = require(driver + '/objectid')
+ , ObjectIdSchema = require('../schema/objectid');
+
+/**
+ * Array of embedded documents
+ * Values always have to be passed to the constructor to initialize, since
+ * otherwise MongooseArray#push will mark the array as modified to the parent.
+ *
+ * @param {Array} values
+ * @param {String} key path
+ * @param {Document} parent document
+ * @api private
+ * @see http://bit.ly/f6CnZU
+ */
+
+function MongooseDocumentArray (values, path, doc) {
+ var arr = [];
+ arr.push.apply(arr, values);
+ arr.__proto__ = MongooseDocumentArray.prototype;
+
+ arr._atomics = {};
+ arr.validators = [];
+ arr._path = path;
+
+ if (doc) {
+ arr._parent = doc;
+ arr._schema = doc.schema.path(path);
+ doc.on('save', arr.notify('save'));
+ doc.on('isNew', arr.notify('isNew'));
+ }
+
+ return arr;
+};
+
+/**
+ * Inherits from MongooseArray
+ */
+
+MongooseDocumentArray.prototype.__proto__ = MongooseArray.prototype;
+
+/**
+ * Overrides cast
+ *
+ * @api private
+ */
+
+MongooseDocumentArray.prototype._cast = function (value) {
+ var doc = new this._schema.caster(value, this);
+ return doc;
+};
+
+/**
+ * Filters items by id
+ *
+ * @param {Object} id
+ * @api public
+ */
+
+MongooseDocumentArray.prototype.id = function (id) {
+ try {
+ var casted = ObjectId.toString(ObjectIdSchema.prototype.cast.call({}, id));
+ } catch (e) {
+ var casted = null;
+ }
+
+ for (var i = 0, l = this.length; i < l; i++) {
+ if (!(this[i].get('_id') instanceof ObjectId)) {
+ if (String(id) == this[i].get('_id').toString())
+ return this[i];
+ } else {
+ if (casted == this[i].get('_id').toString())
+ return this[i];
+ }
+ }
+
+ return null;
+};
+
+/**
+ * Returns an Array and converts any Document
+ * members toObject.
+ *
+ * @return {Array}
+ * @api public
+ */
+
+MongooseDocumentArray.prototype.toObject = function () {
+ return this.map(function (doc) {
+ return doc && doc.toObject() || null;
+ });
+};
+
+/**
+ * Helper for console.log
+ *
+ * @api public
+ */
+
+MongooseDocumentArray.prototype.inspect = function () {
+ return '[' + this.map(function (doc) {
+ return doc && doc.inspect() || 'null';
+ }).join('\n') + ']';
+};
+
+/**
+ * Create fn that notifies all child docs of event.
+ *
+ * @param {String} event
+ * @api private
+ */
+
+MongooseDocumentArray.prototype.notify = function notify (event) {
+ var self = this;
+ return function notify (val) {
+ var i = self.length;
+ while (i--) {
+ self[i].emit(event, val);
+ }
+ }
+}
+
+/**
+ * Module exports.
+ */
+
+module.exports = MongooseDocumentArray;
diff --git a/node_modules/mongoose/lib/types/embedded.js b/node_modules/mongoose/lib/types/embedded.js
new file mode 100644
index 0000000..dff1fe3
--- /dev/null
+++ b/node_modules/mongoose/lib/types/embedded.js
@@ -0,0 +1,103 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Document = require('../document')
+ , inspect = require('util').inspect;
+
+/**
+ * EmbeddedDocument constructor.
+ *
+ * @param {Object} object from db
+ * @param {MongooseDocumentArray} parent array
+ * @api private
+ */
+
+function EmbeddedDocument (obj, parentArr) {
+ this.parentArray = parentArr;
+ this.parent = parentArr._parent;
+ Document.call(this, obj);
+
+ var self = this;
+ this.on('isNew', function (val) {
+ self.isNew = val;
+ });
+};
+
+/**
+ * Inherit from Document
+ */
+
+EmbeddedDocument.prototype.__proto__ = Document.prototype;
+
+/**
+ * Marks parent array as modified
+ *
+ * @api private
+ */
+
+EmbeddedDocument.prototype.commit =
+EmbeddedDocument.prototype.markModified = function (path) {
+ this._activePaths.modify(path);
+
+ if (this.isNew) {
+ // Mark the WHOLE parent array as modified
+ // if this is a new document (i.e., we are initializing
+ // a document),
+ this.parentArray._markModified();
+ } else
+ this.parentArray._markModified(this, path);
+};
+
+/**
+ * Noop. Does not actually save the doc to the db.
+ *
+ * @api public
+ */
+
+EmbeddedDocument.prototype.save = function(fn) {
+ if (fn)
+ fn(null);
+ return this;
+};
+
+/**
+ * Remove the subdocument
+ *
+ * @api public
+ */
+
+EmbeddedDocument.prototype.remove = function (fn) {
+ var _id;
+ if (!this.willRemove) {
+ _id = this._doc._id;
+ if (!_id) {
+ throw new Error('For your own good, Mongoose does not know ' +
+ 'how to remove an EmbeddedDocument that has no _id');
+ }
+ this.parentArray.$pull({ _id: _id });
+ this.willRemove = true;
+ }
+
+ if (fn)
+ fn(null);
+
+ return this;
+};
+
+/**
+ * Helper for console.log
+ *
+ * @api public
+ */
+
+EmbeddedDocument.prototype.inspect = function () {
+ return inspect(this.toObject());
+};
+
+/**
+ * Module exports.
+ */
+
+module.exports = EmbeddedDocument;
diff --git a/node_modules/mongoose/lib/types/index.js b/node_modules/mongoose/lib/types/index.js
new file mode 100644
index 0000000..d07878f
--- /dev/null
+++ b/node_modules/mongoose/lib/types/index.js
@@ -0,0 +1,14 @@
+
+/**
+ * Module exports.
+ */
+
+exports.Array = require('./array');
+exports.Buffer = require('./buffer');
+
+exports.Document = // @deprecate
+exports.Embedded = require('./embedded');
+
+exports.DocumentArray = require('./documentarray');
+exports.Number = require('./number');
+exports.ObjectId = require('./objectid');
diff --git a/node_modules/mongoose/lib/types/number.js b/node_modules/mongoose/lib/types/number.js
new file mode 100644
index 0000000..b2a69b6
--- /dev/null
+++ b/node_modules/mongoose/lib/types/number.js
@@ -0,0 +1,84 @@
+
+/**
+ * Module dependencies.
+ */
+
+/**
+ * MongooseNumber constructor.
+ *
+ * @param {Object} value to pass to Number
+ * @param {Document} parent document
+ * @deprecated (remove in 3.x)
+ * @api private
+ */
+
+function MongooseNumber (value, path, doc) {
+ var number = new Number(value);
+ number.__proto__ = MongooseNumber.prototype;
+ number._atomics = {};
+ number._path = path;
+ number._parent = doc;
+ return number;
+};
+
+/**
+ * Inherits from Number.
+ */
+
+MongooseNumber.prototype = new Number();
+
+/**
+ * Atomic increment
+ *
+ * @api public
+ */
+
+MongooseNumber.prototype.$inc =
+MongooseNumber.prototype.increment = function increment (value) {
+ var schema = this._parent.schema.path(this._path)
+ , value = Number(value) || 1;
+ if (isNaN(value)) value = 1;
+ this._parent.setValue(this._path, schema.cast(this + value));
+ this._parent.getValue(this._path)._atomics['$inc'] = value || 1;
+ this._parent._activePaths.modify(this._path);
+ return this;
+};
+
+/**
+ * Returns true if we have to perform atomics for this, and no normal
+ * operations
+ *
+ * @api public
+ */
+
+MongooseNumber.prototype.__defineGetter__('doAtomics', function () {
+ return Object.keys(this._atomics).length;
+});
+
+/**
+ * Atomic decrement
+ *
+ * @api public
+ */
+
+MongooseNumber.prototype.decrement = function(){
+ this.increment(-1);
+};
+
+/**
+ * Re-declare toString (for `console.log`)
+ *
+ * @api public
+ */
+
+MongooseNumber.prototype.inspect =
+MongooseNumber.prototype.toString = function () {
+ return String(this.valueOf());
+};
+
+
+/**
+ * Module exports
+ */
+
+module.exports = MongooseNumber;
diff --git a/node_modules/mongoose/lib/types/objectid.js b/node_modules/mongoose/lib/types/objectid.js
new file mode 100644
index 0000000..e811fdf
--- /dev/null
+++ b/node_modules/mongoose/lib/types/objectid.js
@@ -0,0 +1,12 @@
+
+/**
+ * Access driver.
+ */
+
+var driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native';
+
+/**
+ * Module exports.
+ */
+
+module.exports = require(driver + '/objectid');
diff --git a/node_modules/mongoose/lib/utils.js b/node_modules/mongoose/lib/utils.js
new file mode 100644
index 0000000..2d8f8ee
--- /dev/null
+++ b/node_modules/mongoose/lib/utils.js
@@ -0,0 +1,434 @@
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = require('events').EventEmitter
+ , ObjectId = require('./types/objectid')
+ , MongooseBuffer
+ , MongooseArray
+ , Document
+
+/**
+ * Produces a collection name from a model name
+ *
+ * @param {String} model name
+ * @return {String} collection name
+ * @api private
+ */
+
+exports.toCollectionName = function (name) {
+ if ('system.profile' === name) return name;
+ if ('system.indexes' === name) return name;
+ return pluralize(name.toLowerCase());
+};
+
+/**
+ * Pluralization rules.
+ */
+
+var rules = [
+ [/(m)an$/gi, '$1en'],
+ [/(pe)rson$/gi, '$1ople'],
+ [/(child)$/gi, '$1ren'],
+ [/^(ox)$/gi, '$1en'],
+ [/(ax|test)is$/gi, '$1es'],
+ [/(octop|vir)us$/gi, '$1i'],
+ [/(alias|status)$/gi, '$1es'],
+ [/(bu)s$/gi, '$1ses'],
+ [/(buffal|tomat|potat)o$/gi, '$1oes'],
+ [/([ti])um$/gi, '$1a'],
+ [/sis$/gi, 'ses'],
+ [/(?:([^f])fe|([lr])f)$/gi, '$1$2ves'],
+ [/(hive)$/gi, '$1s'],
+ [/([^aeiouy]|qu)y$/gi, '$1ies'],
+ [/(x|ch|ss|sh)$/gi, '$1es'],
+ [/(matr|vert|ind)ix|ex$/gi, '$1ices'],
+ [/([m|l])ouse$/gi, '$1ice'],
+ [/(quiz)$/gi, '$1zes'],
+ [/s$/gi, 's'],
+ [/$/gi, 's']
+];
+
+/**
+ * Uncountable words.
+ */
+
+var uncountables = [
+ 'advice',
+ 'energy',
+ 'excretion',
+ 'digestion',
+ 'cooperation',
+ 'health',
+ 'justice',
+ 'labour',
+ 'machinery',
+ 'equipment',
+ 'information',
+ 'pollution',
+ 'sewage',
+ 'paper',
+ 'money',
+ 'species',
+ 'series',
+ 'rain',
+ 'rice',
+ 'fish',
+ 'sheep',
+ 'moose',
+ 'deer',
+ 'news'
+];
+
+/**
+ * Pluralize function.
+ *
+ * @author TJ Holowaychuk (extracted from _ext.js_)
+ * @param {String} string to pluralize
+ * @api private
+ */
+
+function pluralize (str) {
+ var rule, found;
+ if (!~uncountables.indexOf(str.toLowerCase())){
+ found = rules.filter(function(rule){
+ return str.match(rule[0]);
+ });
+ if (found[0]) return str.replace(found[0][0], found[0][1]);
+ }
+ return str;
+};
+
+/**
+ * Add `once` to EventEmitter if absent
+ *
+ * @param {String} event name
+ * @param {Function} listener
+ * @api private
+ */
+
+var Events = EventEmitter;
+
+if (!('once' in EventEmitter.prototype)){
+
+ Events = function () {
+ EventEmitter.apply(this, arguments);
+ };
+
+ /**
+ * Inherit from EventEmitter.
+ */
+
+ Events.prototype.__proto__ = EventEmitter.prototype;
+
+ /**
+ * Add `once`.
+ */
+
+ Events.prototype.once = function (type, listener) {
+ var self = this;
+ self.on(type, function g(){
+ self.removeListener(type, g);
+ listener.apply(this, arguments);
+ });
+ };
+
+}
+
+exports.EventEmitter = Events;
+
+// Modified from node/lib/assert.js
+exports.deepEqual = function deepEqual (a, b) {
+ if (a === b) return true;
+
+ if (a instanceof Date && b instanceof Date)
+ return a.getTime() === b.getTime();
+
+ if (a instanceof ObjectId && b instanceof ObjectId) {
+ return a.toString() === b.toString();
+ }
+
+ if (typeof a !== 'object' && typeof b !== 'object')
+ return a == b;
+
+ if (a === null || b === null || a === undefined || b === undefined)
+ return false
+
+ if (a.prototype !== b.prototype) return false;
+
+ // Handle MongooseNumbers
+ if (a instanceof Number && b instanceof Number) {
+ return a.valueOf() === b.valueOf();
+ }
+
+ if (Buffer.isBuffer(a)) {
+ if (!Buffer.isBuffer(b)) return false;
+ if (a.length !== b.length) return false;
+ for (var i = 0, len = a.length; i < len; ++i) {
+ if (a[i] !== b[i]) return false;
+ }
+ return true;
+ }
+
+ if (isMongooseObject(a)) a = a.toObject();
+ if (isMongooseObject(b)) b = b.toObject();
+
+ try {
+ var ka = Object.keys(a),
+ kb = Object.keys(b),
+ key, i;
+ } 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])) return false;
+ }
+
+ return true;
+};
+
+/**
+ * Object clone with Mongoose natives support.
+ * Creates a minimal data Object.
+ * It does not clone empty Arrays, empty Objects,
+ * and undefined values.
+ * This makes the data payload sent to MongoDB as minimal
+ * as possible.
+ *
+ * @param {Object} object to clone
+ * @param {Object} options - minimize , retainKeyOrder
+ * @return {Object} cloned object
+ * @api private
+ */
+
+var clone = exports.clone = function clone (obj, options) {
+ if (obj === undefined || obj === null)
+ return obj;
+
+ if (Array.isArray(obj))
+ return cloneArray(obj, options);
+
+ if (isMongooseObject(obj)) {
+ if (options && options.json && 'function' === typeof obj.toJSON) {
+ return obj.toJSON(options);
+ } else {
+ return obj.toObject(options);
+ }
+ }
+
+ if ('Object' === obj.constructor.name)
+ return cloneObject(obj, options);
+
+ if ('Date' === obj.constructor.name || 'Function' === obj.constructor.name)
+ return new obj.constructor(+obj);
+
+ if ('RegExp' === obj.constructor.name)
+ return new RegExp(obj.source);
+
+ if (obj instanceof ObjectId)
+ return ObjectId.fromString(ObjectId.toString(obj));
+
+ if (obj.valueOf)
+ return obj.valueOf();
+};
+
+function cloneObject (obj, options) {
+ var retainKeyOrder = options && options.retainKeyOrder
+ , minimize = options && options.minimize
+ , ret = {}
+ , hasKeys
+ , keys
+ , val
+ , k
+ , i
+
+ if (retainKeyOrder) {
+ for (k in obj) {
+ val = clone(obj[k], options);
+
+ if (!minimize || ('undefined' !== typeof val)) {
+ hasKeys || (hasKeys = true);
+ ret[k] = val;
+ }
+ }
+ } else {
+ // faster
+
+ keys = Object.keys(obj);
+ i = keys.length;
+
+ while (i--) {
+ k = keys[i];
+ val = clone(obj[k], options);
+
+ if (!minimize || ('undefined' !== typeof val)) {
+ if (!hasKeys) hasKeys = true;
+ ret[k] = val;
+ }
+ }
+ }
+
+ return minimize
+ ? hasKeys && ret
+ : ret;
+};
+
+function cloneArray (arr, options) {
+ var ret = [];
+ for (var i = 0, l = arr.length; i < l; i++)
+ ret.push(clone(arr[i], options));
+ return ret;
+};
+
+/**
+ * Copies and merges options with defaults.
+ *
+ * @param {Object} defaults
+ * @param {Object} options
+ * @return {Object} (merged) object
+ * @api private
+ */
+
+exports.options = function (defaults, options) {
+ var keys = Object.keys(defaults)
+ , i = keys.length
+ , k ;
+
+ options = options || {};
+
+ while (i--) {
+ k = keys[i];
+ if (!(k in options)) {
+ options[k] = defaults[k];
+ }
+ }
+
+ return options;
+};
+
+/**
+ * Generates a random string
+ *
+ * @api private
+ */
+
+exports.random = function () {
+ return Math.random().toString().substr(3);
+};
+
+exports.inGroupsOf = function inGroupsOf (card, arr, fn) {
+ var group = [];
+ for (var i = 0, l = arr.length; i < l; i++) {
+ if (i && i % card === 0) {
+ fn.apply(this, group);
+ group.length = 0;
+ }
+ group.push(arr[i]);
+ }
+ fn.apply(this, group);
+};
+
+/**
+ * Merges `from` into `to` without overwriting
+ * existing properties of `to`.
+ *
+ * @param {Object} to
+ * @param {Object} from
+ */
+
+exports.merge = function merge (to, from) {
+ var keys = Object.keys(from)
+ , i = keys.length
+ , key
+
+ while (i--) {
+ key = keys[i];
+ if ('undefined' === typeof to[key]) {
+ to[key] = from[key];
+ } else {
+ merge(to[key], from[key]);
+ }
+ }
+};
+
+/**
+ * A faster Array.prototype.slice.call(arguments) alternative
+ */
+
+exports.args = function (args, slice, sliceEnd) {
+ var ret = [];
+ var start = slice || 0;
+ var end = 3 === arguments.length
+ ? sliceEnd
+ : args.length;
+
+ for (var i = start; i < end; ++i) {
+ ret[i - start] = args[i];
+ }
+
+ return ret;
+}
+
+/**
+ * process.nextTick helper.
+ *
+ * Wraps `callback` in a try/catch + nextTick.
+ *
+ * -native has a habit of state corruption
+ * when an error is immediately thrown from within
+ * a collection callback.
+ *
+ * @param {Function} callback
+ * @api private
+ */
+
+exports.tick = function tick (callback) {
+ if ('function' !== typeof callback) return;
+ return function () {
+ try {
+ callback.apply(this, arguments);
+ } catch (err) {
+ // only nextTick on err to get out of
+ // the event loop and avoid state corruption.
+ process.nextTick(function () {
+ throw err;
+ });
+ }
+ }
+}
+
+/**
+ * Returns if `v` is a mongoose object that has
+ * a `toObject()` method we can use. This is for
+ * compatibility with libs like Date.js which do
+ * foolish things to Natives.
+ */
+
+var isMongooseObject = exports.isMongooseObject = function (v) {
+ Document || (Document = require('./document'));
+ MongooseArray || (MongooseArray = require('./types').Array);
+ MongooseBuffer || (MongooseBuffer = require('./types').Buffer);
+
+ return v instanceof Document ||
+ v instanceof MongooseArray ||
+ v instanceof MongooseBuffer
+}
diff --git a/node_modules/mongoose/lib/virtualtype.js b/node_modules/mongoose/lib/virtualtype.js
new file mode 100644
index 0000000..5779df7
--- /dev/null
+++ b/node_modules/mongoose/lib/virtualtype.js
@@ -0,0 +1,74 @@
+/**
+ * VirtualType constructor
+ *
+ * This is what mongoose uses to define virtual attributes via
+ * `Schema.prototype.virtual`
+ *
+ * @api public
+ */
+
+function VirtualType (options) {
+ this.getters = [];
+ this.setters = [];
+ this.options = options || {};
+}
+
+/**
+ * Adds a getter
+ *
+ * @param {Function} fn
+ * @return {VirtualType} this
+ * @api public
+ */
+
+VirtualType.prototype.get = function (fn) {
+ this.getters.push(fn);
+ return this;
+};
+
+/**
+ * Adds a setter
+ *
+ * @param {Function} fn
+ * @return {VirtualType} this
+ * @api public
+ */
+
+VirtualType.prototype.set = function (fn) {
+ this.setters.push(fn);
+ return this;
+};
+
+/**
+ * Applies getters
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @api public
+ */
+
+VirtualType.prototype.applyGetters = function (value, scope) {
+ var v = value;
+ for (var l = this.getters.length - 1; l >= 0; l--){
+ v = this.getters[l].call(scope, v);
+ }
+ return v;
+};
+
+/**
+ * Applies setters
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @api public
+ */
+
+VirtualType.prototype.applySetters = function (value, scope) {
+ var v = value;
+ for (var l = this.setters.length - 1; l >= 0; l--){
+ this.setters[l].call(scope, v);
+ }
+ return v;
+};
+
+module.exports = VirtualType;
diff --git a/node_modules/mongoose/package.json b/node_modules/mongoose/package.json
new file mode 100644
index 0000000..6f8c772
--- /dev/null
+++ b/node_modules/mongoose/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "mongoose",
+ "description": "Mongoose MongoDB ODM",
+ "version": "2.5.13",
+ "author": {
+ "name": "Guillermo Rauch",
+ "email": "guillermo@learnboost.com"
+ },
+ "keywords": [
+ "mongodb",
+ "mongoose",
+ "orm",
+ "data",
+ "datastore",
+ "nosql",
+ "odm",
+ "sql"
+ ],
+ "dependencies": {
+ "hooks": "0.2.0",
+ "mongodb": "0.9.9-7"
+ },
+ "devDependencies": {
+ "should": "0.2.1",
+ "gleak": "0.2.1",
+ "cli-table": "0.0.1"
+ },
+ "directories": {
+ "lib": "./lib/mongoose"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "main": "./index.js",
+ "engines": {
+ "node": ">= 0.4.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/LearnBoost/mongoose.git"
+ },
+ "_id": "mongoose@2.5.13",
+ "optionalDependencies": {},
+ "_engineSupported": true,
+ "_npmVersion": "1.1.12",
+ "_nodeVersion": "v0.6.14",
+ "_defaultsLoaded": true,
+ "dist": {
+ "shasum": "d70f044f8d507d0b8e42f6f8cbc300db7c61304c"
+ },
+ "_from": "mongoose@>= 2.5.1"
+}
diff --git a/node_modules/mongoose/support/expresso/.gitmodules b/node_modules/mongoose/support/expresso/.gitmodules
new file mode 100644
index 0000000..191ddeb
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "deps/jscoverage"]
+ path = deps/jscoverage
+ url = git://github.com/visionmedia/node-jscoverage.git
diff --git a/node_modules/mongoose/support/expresso/.npmignore b/node_modules/mongoose/support/expresso/.npmignore
new file mode 100644
index 0000000..432563f
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/.npmignore
@@ -0,0 +1,3 @@
+.DS_Store
+lib-cov
+*.seed \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/History.md b/node_modules/mongoose/support/expresso/History.md
new file mode 100644
index 0000000..cb16fa9
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/History.md
@@ -0,0 +1,128 @@
+
+0.7.2 / 2010-12-29
+==================
+
+ * Fixed problem with `listen()` sometimes firing on the same tick [guillermo]
+
+0.7.1 / 2010-12-28
+==================
+
+ * Fixed `assert.request()` client logic into an issue() function, fired upon the `listen()` callback if the server doesn't have an assigned fd. [guillermo]
+ * Removed `--watch`
+
+0.7.0 / 2010-11-19
+==================
+
+ * Removed `assert` from test function signature
+ Just use `require('assert')` :) this will make integration
+ with libraries like [should](http://github.com/visionmedia/should) cleaner.
+
+0.6.4 / 2010-11-02
+==================
+
+ * Added regexp support to `assert.response()` headers
+ * Removed `waitForExit` code, causing issues
+
+0.6.3 / 2010-11-02
+==================
+
+ * Added `assert.response()` body RegExp support
+ * Fixed issue with _--serial_ not executing files sequentially. Closes #42
+ * Fixed hang when modules use `setInterval` - monitor running tests & force the process to quit after all have completed + timeout [Steve Mason]
+
+0.6.2 / 2010-09-17
+==================
+
+ * Added _node-jsocoverage_ to package.json (aka will respect npm's binroot)
+ * Added _-t, --timeout_ MS option, defaulting to 2000 ms
+ * Added _-s, --serial_
+ * __PREFIX__ clobberable
+ * Fixed `assert.response()` for latest node
+ * Fixed cov reporting from exploding on empty files
+
+0.6.2 / 2010-08-03
+==================
+
+ * Added `assert.type()`
+ * Renamed `assert.isNotUndefined()` to `assert.isDefined()`
+ * Fixed `assert.includes()` param ordering
+
+0.6.0 / 2010-07-31
+==================
+
+ * Added _docs/api.html_
+ * Added -w, --watch
+ * Added `Array` support to `assert.includes()`
+ * Added; outputting exceptions immediately. Closes #19
+ * Fixed `assert.includes()` param ordering
+ * Fixed `assert.length()` param ordering
+ * Fixed jscoverage links
+
+0.5.0 / 2010-07-16
+==================
+
+ * Added support for async exports
+ * Added timeout support to `assert.response()`. Closes #3
+ * Added 4th arg callback support to `assert.response()`
+ * Added `assert.length()`
+ * Added `assert.match()`
+ * Added `assert.isUndefined()`
+ * Added `assert.isNull()`
+ * Added `assert.includes()`
+ * Added growlnotify support via -g, --growl
+ * Added -o, --only TESTS. Ex: --only "test foo()" --only "test foo(), test bar()"
+ * Removed profanity
+
+0.4.0 / 2010-07-09
+==================
+
+ * Added reporting source coverage (respects --boring for color haters)
+ * Added callback to assert.response(). Closes #12
+ * Fixed; putting exceptions to stderr. Closes #13
+
+0.3.1 / 2010-06-28
+==================
+
+ * Faster assert.response()
+
+0.3.0 / 2010-06-28
+==================
+
+ * Added -p, --port NUM flags
+ * Added assert.response(). Closes #11
+
+0.2.1 / 2010-06-25
+==================
+
+ * Fixed issue with reporting object assertions
+
+0.2.0 / 2010-06-21
+==================
+
+ * Added `make uninstall`
+ * Added better readdir() failure message
+ * Fixed `make install` for kiwi
+
+0.1.0 / 2010-06-15
+==================
+
+ * Added better usage docs via --help
+ * Added better conditional color support
+ * Added pre exit assertion support
+
+0.0.3 / 2010-06-02
+==================
+
+ * Added more room for filenames in test coverage
+ * Added boring output support via --boring (suppress colored output)
+ * Fixed async failure exit status
+
+0.0.2 / 2010-05-30
+==================
+
+ * Fixed exit status for CI support
+
+0.0.1 / 2010-05-30
+==================
+
+ * Initial release \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/Makefile b/node_modules/mongoose/support/expresso/Makefile
new file mode 100644
index 0000000..8acfe56
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/Makefile
@@ -0,0 +1,53 @@
+
+PREFIX ?= /usr/local
+BIN = bin/expresso
+JSCOV = deps/jscoverage/node-jscoverage
+DOCS = docs/index.md
+HTMLDOCS = $(DOCS:.md=.html)
+
+test: $(BIN)
+ @./$(BIN) -I lib --growl $(TEST_FLAGS) test/*.test.js
+
+test-cov:
+ @./$(BIN) -I lib --cov $(TEST_FLAGS) test/*.test.js
+
+test-serial:
+ @./$(BIN) --serial -I lib $(TEST_FLAGS) test/serial/*.test.js
+
+install: install-jscov install-expresso
+
+uninstall:
+ rm -f $(PREFIX)/bin/expresso
+ rm -f $(PREFIX)/bin/node-jscoverage
+
+install-jscov: $(JSCOV)
+ install $(JSCOV) $(PREFIX)/bin
+
+install-expresso:
+ install $(BIN) $(PREFIX)/bin
+
+$(JSCOV):
+ cd deps/jscoverage && ./configure && make && mv jscoverage node-jscoverage
+
+clean:
+ @cd deps/jscoverage && git clean -fd
+
+docs: docs/api.html $(HTMLDOCS)
+
+%.html: %.md
+ @echo "... $< > $@"
+ @ronn -5 --pipe --fragment $< \
+ | cat docs/layout/head.html - docs/layout/foot.html \
+ > $@
+
+docs/api.html: bin/expresso
+ dox \
+ --title "Expresso" \
+ --ribbon "http://github.com/visionmedia/expresso" \
+ --desc "Insanely fast TDD framework for [node](http://nodejs.org) featuring code coverage reporting." \
+ $< > $@
+
+docclean:
+ rm -f docs/*.html
+
+.PHONY: test test-cov install uninstall install-expresso install-jscov clean docs docclean \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/Readme.md b/node_modules/mongoose/support/expresso/Readme.md
new file mode 100644
index 0000000..05c972e
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/Readme.md
@@ -0,0 +1,61 @@
+
+# Expresso
+
+ TDD framework for [nodejs](http://nodejs.org).
+
+## Features
+
+ - light-weight
+ - intuitive async support
+ - intuitive test runner executable
+ - test coverage support and reporting
+ - uses the _assert_ module
+ - `assert.eql()` alias of `assert.deepEqual()`
+ - `assert.response()` http response utility
+ - `assert.includes()`
+ - `assert.type()`
+ - `assert.isNull()`
+ - `assert.isUndefined()`
+ - `assert.isNotNull()`
+ - `assert.isDefined()`
+ - `assert.match()`
+ - `assert.length()`
+
+## Installation
+
+To install both expresso _and_ node-jscoverage run:
+
+ $ make install
+
+To install expresso alone (no build required) run:
+
+ $ make install-expresso
+
+Install via npm:
+
+ $ npm install expresso
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2010 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
+
+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.
diff --git a/node_modules/mongoose/support/expresso/bin/expresso b/node_modules/mongoose/support/expresso/bin/expresso
new file mode 100755
index 0000000..fd98fff
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/bin/expresso
@@ -0,0 +1,874 @@
+#!/usr/bin/env node
+
+/*
+ * Expresso
+ * Copyright(c) TJ Holowaychuk <tj@vision-media.ca>
+ * (MIT Licensed)
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var assert = require('assert'),
+ childProcess = require('child_process'),
+ http = require('http'),
+ path = require('path'),
+ sys = require('sys'),
+ cwd = process.cwd(),
+ fs = require('fs'),
+ defer;
+
+/**
+ * Expresso version.
+ */
+
+var version = '0.7.2';
+
+/**
+ * Failure count.
+ */
+
+var failures = 0;
+
+
+/**
+ * Number of tests executed.
+ */
+
+var testcount = 0;
+
+/**
+ * Whitelist of tests to run.
+ */
+
+var only = [];
+
+/**
+ * Boring output.
+ */
+
+var boring = false;
+
+/**
+ * Growl notifications.
+ */
+
+var growl = false;
+
+/**
+ * Server port.
+ */
+
+var port = 5555;
+
+/**
+ * Execute serially.
+ */
+
+var serial = false;
+
+/**
+ * Default timeout.
+ */
+
+var timeout = 2000;
+
+/**
+ * Quiet output.
+ */
+
+var quiet = false;
+
+/**
+ * Usage documentation.
+ */
+
+var usage = ''
+ + '[bold]{Usage}: expresso [options] <file ...>'
+ + '\n'
+ + '\n[bold]{Options}:'
+ + '\n -g, --growl Enable growl notifications'
+ + '\n -c, --coverage Generate and report test coverage'
+ + '\n -q, --quiet Suppress coverage report if 100%'
+ + '\n -t, --timeout MS Timeout in milliseconds, defaults to 2000'
+ + '\n -r, --require PATH Require the given module path'
+ + '\n -o, --only TESTS Execute only the comma sperated TESTS (can be set several times)'
+ + '\n -I, --include PATH Unshift the given path to require.paths'
+ + '\n -p, --port NUM Port number for test servers, starts at 5555'
+ + '\n -s, --serial Execute tests serially'
+ + '\n -b, --boring Suppress ansi-escape colors'
+ + '\n -v, --version Output version number'
+ + '\n -h, --help Display help information'
+ + '\n';
+
+// Parse arguments
+
+var files = [],
+ args = process.argv.slice(2);
+
+while (args.length) {
+ var arg = args.shift();
+ switch (arg) {
+ case '-h':
+ case '--help':
+ print(usage + '\n');
+ process.exit(1);
+ break;
+ case '-v':
+ case '--version':
+ sys.puts(version);
+ process.exit(1);
+ break;
+ case '-i':
+ case '-I':
+ case '--include':
+ if (arg = args.shift()) {
+ require.paths.unshift(arg);
+ } else {
+ throw new Error('--include requires a path');
+ }
+ break;
+ case '-o':
+ case '--only':
+ if (arg = args.shift()) {
+ only = only.concat(arg.split(/ *, */));
+ } else {
+ throw new Error('--only requires comma-separated test names');
+ }
+ break;
+ case '-p':
+ case '--port':
+ if (arg = args.shift()) {
+ port = parseInt(arg, 10);
+ } else {
+ throw new Error('--port requires a number');
+ }
+ break;
+ case '-r':
+ case '--require':
+ if (arg = args.shift()) {
+ require(arg);
+ } else {
+ throw new Error('--require requires a path');
+ }
+ break;
+ case '-t':
+ case '--timeout':
+ if (arg = args.shift()) {
+ timeout = parseInt(arg, 10);
+ } else {
+ throw new Error('--timeout requires an argument');
+ }
+ break;
+ case '-c':
+ case '--cov':
+ case '--coverage':
+ defer = true;
+ childProcess.exec('rm -fr lib-cov && node-jscoverage lib lib-cov', function(err){
+ if (err) throw err;
+ require.paths.unshift('lib-cov');
+ run(files);
+ })
+ break;
+ case '-q':
+ case '--quiet':
+ quiet = true;
+ break;
+ case '-b':
+ case '--boring':
+ boring = true;
+ break;
+ case '-g':
+ case '--growl':
+ growl = true;
+ break;
+ case '-s':
+ case '--serial':
+ serial = true;
+ break;
+ default:
+ if (/\.js$/.test(arg)) {
+ files.push(arg);
+ }
+ break;
+ }
+}
+
+/**
+ * Colorized sys.error().
+ *
+ * @param {String} str
+ */
+
+function print(str){
+ sys.error(colorize(str));
+}
+
+/**
+ * Colorize the given string using ansi-escape sequences.
+ * Disabled when --boring is set.
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+function colorize(str){
+ var colors = { bold: 1, red: 31, green: 32, yellow: 33 };
+ return str.replace(/\[(\w+)\]\{([^]*?)\}/g, function(_, color, str){
+ return boring
+ ? str
+ : '\x1B[' + colors[color] + 'm' + str + '\x1B[0m';
+ });
+}
+
+// Alias deepEqual as eql for complex equality
+
+assert.eql = assert.deepEqual;
+
+/**
+ * Assert that `val` is null.
+ *
+ * @param {Mixed} val
+ * @param {String} msg
+ */
+
+assert.isNull = function(val, msg) {
+ assert.strictEqual(null, val, msg);
+};
+
+/**
+ * Assert that `val` is not null.
+ *
+ * @param {Mixed} val
+ * @param {String} msg
+ */
+
+assert.isNotNull = function(val, msg) {
+ assert.notStrictEqual(null, val, msg);
+};
+
+/**
+ * Assert that `val` is undefined.
+ *
+ * @param {Mixed} val
+ * @param {String} msg
+ */
+
+assert.isUndefined = function(val, msg) {
+ assert.strictEqual(undefined, val, msg);
+};
+
+/**
+ * Assert that `val` is not undefined.
+ *
+ * @param {Mixed} val
+ * @param {String} msg
+ */
+
+assert.isDefined = function(val, msg) {
+ assert.notStrictEqual(undefined, val, msg);
+};
+
+/**
+ * Assert that `obj` is `type`.
+ *
+ * @param {Mixed} obj
+ * @param {String} type
+ * @api public
+ */
+
+assert.type = function(obj, type, msg){
+ var real = typeof obj;
+ msg = msg || 'typeof ' + sys.inspect(obj) + ' is ' + real + ', expected ' + type;
+ assert.ok(type === real, msg);
+};
+
+/**
+ * Assert that `str` matches `regexp`.
+ *
+ * @param {String} str
+ * @param {RegExp} regexp
+ * @param {String} msg
+ */
+
+assert.match = function(str, regexp, msg) {
+ msg = msg || sys.inspect(str) + ' does not match ' + sys.inspect(regexp);
+ assert.ok(regexp.test(str), msg);
+};
+
+/**
+ * Assert that `val` is within `obj`.
+ *
+ * Examples:
+ *
+ * assert.includes('foobar', 'bar');
+ * assert.includes(['foo', 'bar'], 'foo');
+ *
+ * @param {String|Array} obj
+ * @param {Mixed} val
+ * @param {String} msg
+ */
+
+assert.includes = function(obj, val, msg) {
+ msg = msg || sys.inspect(obj) + ' does not include ' + sys.inspect(val);
+ assert.ok(obj.indexOf(val) >= 0, msg);
+};
+
+/**
+ * Assert length of `val` is `n`.
+ *
+ * @param {Mixed} val
+ * @param {Number} n
+ * @param {String} msg
+ */
+
+assert.length = function(val, n, msg) {
+ msg = msg || sys.inspect(val) + ' has length of ' + val.length + ', expected ' + n;
+ assert.equal(n, val.length, msg);
+};
+
+/**
+ * Assert response from `server` with
+ * the given `req` object and `res` assertions object.
+ *
+ * @param {Server} server
+ * @param {Object} req
+ * @param {Object|Function} res
+ * @param {String} msg
+ */
+
+assert.response = function(server, req, res, msg){
+ // Check that the server is ready or defer
+ if (!server.fd) {
+ if (!('__deferred' in server)) {
+ server.__deferred = [];
+ }
+ server.__deferred.push(arguments);
+ if (!server.__started) {
+ server.listen(server.__port = port++, '127.0.0.1', function(){
+ if (server.__deferred) {
+ process.nextTick(function(){
+ server.__deferred.forEach(function(args){
+ assert.response.apply(assert, args);
+ });
+ });
+ }
+ });
+ server.__started = true;
+ }
+ return;
+ }
+
+ // Callback as third or fourth arg
+ var callback = typeof res === 'function'
+ ? res
+ : typeof msg === 'function'
+ ? msg
+ : function(){};
+
+ // Default messate to test title
+ if (typeof msg === 'function') msg = null;
+ msg = msg || assert.testTitle;
+ msg += '. ';
+
+ // Pending responses
+ server.__pending = server.__pending || 0;
+ server.__pending++;
+
+ // Create client
+ if (!server.fd) {
+ server.listen(server.__port = port++, '127.0.0.1', issue);
+ } else {
+ issue();
+ }
+
+ function issue(){
+ if (!server.client)
+ server.client = http.createClient(server.__port);
+
+ // Issue request
+ var timer,
+ client = server.client,
+ method = req.method || 'GET',
+ status = res.status || res.statusCode,
+ data = req.data || req.body,
+ requestTimeout = req.timeout || 0;
+
+ var request = client.request(method, req.url, req.headers);
+
+ // Timeout
+ if (requestTimeout) {
+ timer = setTimeout(function(){
+ --server.__pending || server.close();
+ delete req.timeout;
+ assert.fail(msg + 'Request timed out after ' + requestTimeout + 'ms.');
+ }, requestTimeout);
+ }
+
+ if (data) request.write(data);
+ request.addListener('response', function(response){
+ response.body = '';
+ response.setEncoding('utf8');
+ response.addListener('data', function(chunk){ response.body += chunk; });
+ response.addListener('end', function(){
+ --server.__pending || server.close();
+ if (timer) clearTimeout(timer);
+
+ // Assert response body
+ if (res.body !== undefined) {
+ var eql = res.body instanceof RegExp
+ ? res.body.test(response.body)
+ : res.body === response.body;
+ assert.ok(
+ eql,
+ msg + 'Invalid response body.\n'
+ + ' Expected: ' + sys.inspect(res.body) + '\n'
+ + ' Got: ' + sys.inspect(response.body)
+ );
+ }
+
+ // Assert response status
+ if (typeof status === 'number') {
+ assert.equal(
+ response.statusCode,
+ status,
+ msg + colorize('Invalid response status code.\n'
+ + ' Expected: [green]{' + status + '}\n'
+ + ' Got: [red]{' + response.statusCode + '}')
+ );
+ }
+
+ // Assert response headers
+ if (res.headers) {
+ var keys = Object.keys(res.headers);
+ for (var i = 0, len = keys.length; i < len; ++i) {
+ var name = keys[i],
+ actual = response.headers[name.toLowerCase()],
+ expected = res.headers[name],
+ eql = expected instanceof RegExp
+ ? expected.test(actual)
+ : expected == actual;
+ assert.ok(
+ eql,
+ msg + colorize('Invalid response header [bold]{' + name + '}.\n'
+ + ' Expected: [green]{' + expected + '}\n'
+ + ' Got: [red]{' + actual + '}')
+ );
+ }
+ }
+
+ // Callback
+ callback(response);
+ });
+ });
+ request.end();
+ }
+};
+
+/**
+ * Pad the given string to the maximum width provided.
+ *
+ * @param {String} str
+ * @param {Number} width
+ * @return {String}
+ */
+
+function lpad(str, width) {
+ str = String(str);
+ var n = width - str.length;
+ if (n < 1) return str;
+ while (n--) str = ' ' + str;
+ return str;
+}
+
+/**
+ * Pad the given string to the maximum width provided.
+ *
+ * @param {String} str
+ * @param {Number} width
+ * @return {String}
+ */
+
+function rpad(str, width) {
+ str = String(str);
+ var n = width - str.length;
+ if (n < 1) return str;
+ while (n--) str = str + ' ';
+ return str;
+}
+
+/**
+ * Report test coverage.
+ *
+ * @param {Object} cov
+ */
+
+function reportCoverage(cov) {
+ // Stats
+ print('\n [bold]{Test Coverage}\n');
+ var sep = ' +------------------------------------------+----------+------+------+--------+',
+ lastSep = ' +----------+------+------+--------+';
+ sys.puts(sep);
+ sys.puts(' | filename | coverage | LOC | SLOC | missed |');
+ sys.puts(sep);
+ for (var name in cov) {
+ var file = cov[name];
+ if (Array.isArray(file)) {
+ sys.print(' | ' + rpad(name, 40));
+ sys.print(' | ' + lpad(file.coverage.toFixed(2), 8));
+ sys.print(' | ' + lpad(file.LOC, 4));
+ sys.print(' | ' + lpad(file.SLOC, 4));
+ sys.print(' | ' + lpad(file.totalMisses, 6));
+ sys.print(' |\n');
+ }
+ }
+ sys.puts(sep);
+ sys.print(' ' + rpad('', 40));
+ sys.print(' | ' + lpad(cov.coverage.toFixed(2), 8));
+ sys.print(' | ' + lpad(cov.LOC, 4));
+ sys.print(' | ' + lpad(cov.SLOC, 4));
+ sys.print(' | ' + lpad(cov.totalMisses, 6));
+ sys.print(' |\n');
+ sys.puts(lastSep);
+ // Source
+ for (var name in cov) {
+ if (name.match(/\.js$/)) {
+ var file = cov[name];
+ if ((file.coverage < 100) || !quiet) {
+ print('\n [bold]{' + name + '}:');
+ print(file.source);
+ sys.print('\n');
+ }
+ }
+ }
+}
+
+/**
+ * Populate code coverage data.
+ *
+ * @param {Object} cov
+ */
+
+function populateCoverage(cov) {
+ cov.LOC =
+ cov.SLOC =
+ cov.totalFiles =
+ cov.totalHits =
+ cov.totalMisses =
+ cov.coverage = 0;
+ for (var name in cov) {
+ var file = cov[name];
+ if (Array.isArray(file)) {
+ // Stats
+ ++cov.totalFiles;
+ cov.totalHits += file.totalHits = coverage(file, true);
+ cov.totalMisses += file.totalMisses = coverage(file, false);
+ file.totalLines = file.totalHits + file.totalMisses;
+ cov.SLOC += file.SLOC = file.totalLines;
+ if (!file.source) file.source = [];
+ cov.LOC += file.LOC = file.source.length;
+ file.coverage = (file.totalHits / file.totalLines) * 100;
+ // Source
+ var width = file.source.length.toString().length;
+ file.source = file.source.map(function(line, i){
+ ++i;
+ var hits = file[i] === 0 ? 0 : (file[i] || ' ');
+ if (!boring) {
+ if (hits === 0) {
+ hits = '\x1b[31m' + hits + '\x1b[0m';
+ line = '\x1b[41m' + line + '\x1b[0m';
+ } else {
+ hits = '\x1b[32m' + hits + '\x1b[0m';
+ }
+ }
+ return '\n ' + lpad(i, width) + ' | ' + hits + ' | ' + line;
+ }).join('');
+ }
+ }
+ cov.coverage = (cov.totalHits / cov.SLOC) * 100;
+}
+
+/**
+ * Total coverage for the given file data.
+ *
+ * @param {Array} data
+ * @return {Type}
+ */
+
+function coverage(data, val) {
+ var n = 0;
+ for (var i = 0, len = data.length; i < len; ++i) {
+ if (data[i] !== undefined && data[i] == val) ++n;
+ }
+ return n;
+}
+
+/**
+ * Test if all files have 100% coverage
+ *
+ * @param {Object} cov
+ * @return {Boolean}
+ */
+
+function hasFullCoverage(cov) {
+ for (var name in cov) {
+ var file = cov[name];
+ if (file instanceof Array) {
+ if (file.coverage !== 100) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * Run the given test `files`, or try _test/*_.
+ *
+ * @param {Array} files
+ */
+
+function run(files) {
+ cursor(false);
+ if (!files.length) {
+ try {
+ files = fs.readdirSync('test').map(function(file){
+ return 'test/' + file;
+ });
+ } catch (err) {
+ print('\n failed to load tests in [bold]{./test}\n');
+ ++failures;
+ process.exit(1);
+ }
+ }
+ runFiles(files);
+}
+
+/**
+ * Show the cursor when `show` is true, otherwise hide it.
+ *
+ * @param {Boolean} show
+ */
+
+function cursor(show) {
+ if (show) {
+ sys.print('\x1b[?25h');
+ } else {
+ sys.print('\x1b[?25l');
+ }
+}
+
+/**
+ * Run the given test `files`.
+ *
+ * @param {Array} files
+ */
+
+function runFiles(files) {
+ if (serial) {
+ (function next(){
+ if (files.length) {
+ runFile(files.shift(), next);
+ }
+ })();
+ } else {
+ files.forEach(runFile);
+ }
+}
+
+/**
+ * Run tests for the given `file`, callback `fn()` when finished.
+ *
+ * @param {String} file
+ * @param {Function} fn
+ */
+
+function runFile(file, fn) {
+ if (file.match(/\.js$/)) {
+ var title = path.basename(file),
+ file = path.join(cwd, file),
+ mod = require(file.replace(/\.js$/, ''));
+ (function check(){
+ var len = Object.keys(mod).length;
+ if (len) {
+ runSuite(title, mod, fn);
+ } else {
+ setTimeout(check, 20);
+ }
+ })();
+ }
+}
+
+/**
+ * Report `err` for the given `test` and `suite`.
+ *
+ * @param {String} suite
+ * @param {String} test
+ * @param {Error} err
+ */
+
+function error(suite, test, err) {
+ ++failures;
+ var name = err.name,
+ stack = err.stack.replace(err.name, ''),
+ label = test === 'uncaught'
+ ? test
+ : suite + ' ' + test;
+ print('\n [bold]{' + label + '}: [red]{' + name + '}' + stack + '\n');
+}
+
+/**
+ * Run the given tests, callback `fn()` when finished.
+ *
+ * @param {String} title
+ * @param {Object} tests
+ * @param {Function} fn
+ */
+
+var dots = 0;
+function runSuite(title, tests, fn) {
+ // Keys
+ var keys = only.length
+ ? only.slice(0)
+ : Object.keys(tests);
+
+ // Setup
+ var setup = tests.setup || function(fn){ fn(); };
+
+ // Iterate tests
+ (function next(){
+ if (keys.length) {
+ var key,
+ test = tests[key = keys.shift()];
+ // Non-tests
+ if (key === 'setup') return next();
+
+ // Run test
+ if (test) {
+ try {
+ ++testcount;
+ assert.testTitle = key;
+ if (serial) {
+ sys.print('.');
+ if (++dots % 25 === 0) sys.print('\n');
+ setup(function(){
+ if (test.length < 1) {
+ test();
+ next();
+ } else {
+ var id = setTimeout(function(){
+ throw new Error("'" + key + "' timed out");
+ }, timeout);
+ test(function(){
+ clearTimeout(id);
+ next();
+ });
+ }
+ });
+ } else {
+ test(function(fn){
+ addBeforeExit(function(){
+ try {
+ fn();
+ } catch (err) {
+ error(title, key, err);
+ }
+ });
+ });
+ }
+ } catch (err) {
+ error(title, key, err);
+ }
+ }
+ if (!serial) next();
+ } else if (serial) {
+ fn();
+ }
+ })();
+}
+
+/**
+ * Adds before exit handlers with only one event handler
+ *
+ * @param {Function} callback
+ * @api private
+ */
+
+var beforeExitHandlers = [];
+
+function addBeforeExit (fn) {
+ if (!beforeExitHandlers.length)
+ process.on('beforeExit', function () {
+ for (var i = 0, l = beforeExitHandlers.length; i < l; i++)
+ beforeExitHandlers[i]();
+ });
+ beforeExitHandlers.push(fn);
+};
+
+/**
+ * Report exceptions.
+ */
+
+function report() {
+ cursor(true);
+ process.emit('beforeExit');
+ if (failures) {
+ print('\n [bold]{Failures}: [red]{' + failures + '}\n\n');
+ notify('Failures: ' + failures);
+ } else {
+ if (serial) print('');
+ print('\n [green]{100%} ' + testcount + ' tests\n');
+ notify('100% ok');
+ }
+ if (typeof _$jscoverage === 'object') {
+ populateCoverage(_$jscoverage);
+ if (!hasFullCoverage(_$jscoverage) || !quiet) {
+ reportCoverage(_$jscoverage);
+ }
+ }
+}
+
+/**
+ * Growl notify the given `msg`.
+ *
+ * @param {String} msg
+ */
+
+function notify(msg) {
+ if (growl) {
+ childProcess.exec('growlnotify -name Expresso -m "' + msg + '"');
+ }
+}
+
+// Report uncaught exceptions
+
+process.addListener('uncaughtException', function(err){
+ error('uncaught', 'uncaught', err);
+});
+
+// Show cursor
+
+['INT', 'TERM', 'QUIT'].forEach(function(sig){
+ process.addListener('SIG' + sig, function(){
+ cursor(true);
+ process.exit(1);
+ });
+});
+
+// Report test coverage when available
+// and emit "beforeExit" event to perform
+// final assertions
+
+var orig = process.emit;
+process.emit = function(event){
+ if (event === 'exit') {
+ report();
+ process.reallyExit(failures);
+ }
+ orig.apply(this, arguments);
+};
+
+// Run test files
+
+if (!defer) run(files);
diff --git a/node_modules/mongoose/support/expresso/docs/api.html b/node_modules/mongoose/support/expresso/docs/api.html
new file mode 100644
index 0000000..7b8fb2b
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/docs/api.html
@@ -0,0 +1,1080 @@
+<a href="http://github.com/visionmedia/expresso"><img alt="Fork me on GitHub" id="ribbon" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"></a><html>
+ <head>
+ <title>Expresso</title>
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
+ <style>body {
+ margin: 0;
+ padding: 0;
+ font: 14px/1.5 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
+ color: #252519;
+}
+a {
+ color: #252519;
+}
+a:hover {
+ text-decoration: underline;
+ color: #19469D;
+}
+p {
+ margin: 12px 0;
+}
+h1, h2, h3 {
+ margin: 0;
+ padding: 0;
+}
+table#source {
+ width: 100%;
+ border-collapse: collapse;
+}
+table#source td:first-child {
+ padding: 30px 40px 30px 40px;
+ vertical-align: top;
+}
+table#source td:first-child,
+table#source td:first-child pre {
+ width: 450px;
+}
+table#source td:last-child {
+ padding: 30px 0 30px 40px;
+ border-left: 1px solid #E5E5EE;
+ background: #F5F5FF;
+}
+table#source tr {
+ border-bottom: 1px solid #E5E5EE;
+}
+table#source tr.filename {
+ padding-top: 40px;
+ border-top: 1px solid #E5E5EE;
+}
+table#source tr.filename td:first-child {
+ text-transform: capitalize;
+}
+table#source tr.filename td:last-child {
+ font-size: 12px;
+}
+table#source tr.filename h2 {
+ margin: 0;
+ padding: 0;
+ cursor: pointer;
+}
+table#source tr.code h1,
+table#source tr.code h2,
+table#source tr.code h3 {
+ margin-top: 30px;
+ font-family: "Lucida Grande", "Helvetica Nueue", Arial, sans-serif;
+ font-size: 18px;
+}
+table#source tr.code h2 {
+ font-size: 16px;
+}
+table#source tr.code h3 {
+ font-size: 14px;
+}
+table#source tr.code ul {
+ margin: 15px 0 15px 35px;
+ padding: 0;
+}
+table#source tr.code ul li {
+ margin: 0;
+ padding: 1px 0;
+}
+table#source tr.code ul li p {
+ margin: 0;
+ padding: 0;
+}
+table#source tr.code td:first-child pre {
+ padding: 20px;
+}
+#ribbon {
+ position: fixed;
+ top: 0;
+ right: 0;
+}
+code .string { color: #219161; }
+code .regexp { color: #219161; }
+code .keyword { color: #954121; }
+code .number { color: #19469D; }
+code .comment { color: #bbb; }
+code .this { color: #19469D; }</style>
+ <script>
+ $(function(){
+ $('tr.code').hide();
+ $('tr.filename').toggle(function(){
+ $(this).nextUntil('.filename').fadeIn();
+ }, function(){
+ $(this).nextUntil('.filename').fadeOut();
+ });
+ });
+ </script>
+ </head>
+ <body>
+<table id="source"><tbody><tr><td><h1>Expresso</h1><p>Insanely fast TDD framework for <a href="http://nodejs.org">node</a> featuring code coverage reporting.</p></td><td></td></tr><tr class="filename"><td><h2 id="bin/expresso"><a href="#">expresso</a></h2></td><td>bin/expresso</td></tr><tr class="code">
+<td class="docs">
+<h1>!/usr/bin/env node</h1>
+</td>
+<td class="code">
+<pre><code>
+ * <span class="class">Expresso</span>
+ * <span class="class">Copyright</span>(<span class="variable">c</span>) <span class="class">TJ</span> <span class="class">Holowaychuk</span> &<span class="variable">lt</span>;<span class="variable">tj</span>@<span class="variable">vision</span>-<span class="variable">media</span>.<span class="variable">ca</span>&<span class="variable">gt</span>;
+ * (<span class="class">MIT</span> <span class="class">Licensed</span>)
+ </code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Module dependencies.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">assert</span> = <span class="variable">require</span>(<span class="string">'assert'</span>),
+ <span class="variable">childProcess</span> = <span class="variable">require</span>(<span class="string">'child_process'</span>),
+ <span class="variable">http</span> = <span class="variable">require</span>(<span class="string">'http'</span>),
+ <span class="variable">path</span> = <span class="variable">require</span>(<span class="string">'path'</span>),
+ <span class="variable">sys</span> = <span class="variable">require</span>(<span class="string">'sys'</span>),
+ <span class="variable">cwd</span> = <span class="variable">process</span>.<span class="variable">cwd</span>(),
+ <span class="variable">fs</span> = <span class="variable">require</span>(<span class="string">'fs'</span>),
+ <span class="variable">defer</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Expresso version.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">version</span> = <span class="string">'0.6.4'</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Failure count.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">failures</span> = <span class="number integer">0</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Number of tests executed.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">testcount</span> = <span class="number integer">0</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Whitelist of tests to run.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">only</span> = [];</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Boring output.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">boring</span> = <span class="variable">false</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Growl notifications.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">growl</span> = <span class="variable">false</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Server port.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">port</span> = <span class="number integer">5555</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Watch mode.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">watch</span> = <span class="variable">false</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Execute serially.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">serial</span> = <span class="variable">false</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Default timeout.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">timeout</span> = <span class="number integer">2000</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Usage documentation.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">usage</span> = <span class="string">''</span>
+ + <span class="string">'[bold]{Usage}: expresso [options] &lt;file ...&gt;'</span>
+ + <span class="string">'\n'</span>
+ + <span class="string">'\n[bold]{Options}:'</span>
+ + <span class="string">'\n -w, --watch Watch for modifications and re-execute tests'</span>
+ + <span class="string">'\n -g, --growl Enable growl notifications'</span>
+ + <span class="string">'\n -c, --coverage Generate and report test coverage'</span>
+ + <span class="string">'\n -t, --timeout MS Timeout in milliseconds, defaults to 2000'</span>
+ + <span class="string">'\n -r, --require PATH Require the given module path'</span>
+ + <span class="string">'\n -o, --only TESTS Execute only the comma sperated TESTS (can be set several times)'</span>
+ + <span class="string">'\n -I, --include PATH Unshift the given path to require.paths'</span>
+ + <span class="string">'\n -p, --port NUM Port number for test servers, starts at 5555'</span>
+ + <span class="string">'\n -s, --serial Execute tests serially'</span>
+ + <span class="string">'\n -b, --boring Suppress ansi-escape colors'</span>
+ + <span class="string">'\n -v, --version Output version number'</span>
+ + <span class="string">'\n -h, --help Display help information'</span>
+ + <span class="string">'\n'</span>;
+
+<span class="comment">// Parse arguments</span>
+
+<span class="keyword">var</span> <span class="variable">files</span> = [],
+ <span class="variable">args</span> = <span class="variable">process</span>.<span class="variable">argv</span>.<span class="variable">slice</span>(<span class="number integer">2</span>);
+
+<span class="keyword">while</span> (<span class="variable">args</span>.<span class="variable">length</span>) {
+ <span class="keyword">var</span> <span class="variable">arg</span> = <span class="variable">args</span>.<span class="variable">shift</span>();
+ <span class="keyword">switch</span> (<span class="variable">arg</span>) {
+ <span class="keyword">case</span> <span class="string">'-h'</span>:
+ <span class="keyword">case</span> <span class="string">'--help'</span>:
+ <span class="variable">print</span>(<span class="variable">usage</span> + <span class="string">'\n'</span>);
+ <span class="variable">process</span>.<span class="variable">exit</span>(<span class="number integer">1</span>);
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-v'</span>:
+ <span class="keyword">case</span> <span class="string">'--version'</span>:
+ <span class="variable">sys</span>.<span class="variable">puts</span>(<span class="variable">version</span>);
+ <span class="variable">process</span>.<span class="variable">exit</span>(<span class="number integer">1</span>);
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-i'</span>:
+ <span class="keyword">case</span> <span class="string">'-I'</span>:
+ <span class="keyword">case</span> <span class="string">'--include'</span>:
+ <span class="keyword">if</span> (<span class="variable">arg</span> = <span class="variable">args</span>.<span class="variable">shift</span>()) {
+ <span class="variable">require</span>.<span class="variable">paths</span>.<span class="variable">unshift</span>(<span class="variable">arg</span>);
+ } <span class="keyword">else</span> {
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="class">Error</span>(<span class="string">'--include requires a path'</span>);
+ }
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-o'</span>:
+ <span class="keyword">case</span> <span class="string">'--only'</span>:
+ <span class="keyword">if</span> (<span class="variable">arg</span> = <span class="variable">args</span>.<span class="variable">shift</span>()) {
+ <span class="variable">only</span> = <span class="variable">only</span>.<span class="variable">concat</span>(<span class="variable">arg</span>.<span class="variable">split</span>(<span class="regexp">/ *, */</span>));
+ } <span class="keyword">else</span> {
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="class">Error</span>(<span class="string">'--only requires comma-separated test names'</span>);
+ }
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-p'</span>:
+ <span class="keyword">case</span> <span class="string">'--port'</span>:
+ <span class="keyword">if</span> (<span class="variable">arg</span> = <span class="variable">args</span>.<span class="variable">shift</span>()) {
+ <span class="variable">port</span> = <span class="variable">parseInt</span>(<span class="variable">arg</span>, <span class="number integer">10</span>);
+ } <span class="keyword">else</span> {
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="class">Error</span>(<span class="string">'--port requires a number'</span>);
+ }
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-r'</span>:
+ <span class="keyword">case</span> <span class="string">'--require'</span>:
+ <span class="keyword">if</span> (<span class="variable">arg</span> = <span class="variable">args</span>.<span class="variable">shift</span>()) {
+ <span class="variable">require</span>(<span class="variable">arg</span>);
+ } <span class="keyword">else</span> {
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="class">Error</span>(<span class="string">'--require requires a path'</span>);
+ }
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-t'</span>:
+ <span class="keyword">case</span> <span class="string">'--timeout'</span>:
+ <span class="keyword">if</span> (<span class="variable">arg</span> = <span class="variable">args</span>.<span class="variable">shift</span>()) {
+ <span class="variable">timeout</span> = <span class="variable">parseInt</span>(<span class="variable">arg</span>, <span class="number integer">10</span>);
+ } <span class="keyword">else</span> {
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="class">Error</span>(<span class="string">'--timeout requires an argument'</span>);
+ }
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-c'</span>:
+ <span class="keyword">case</span> <span class="string">'--cov'</span>:
+ <span class="keyword">case</span> <span class="string">'--coverage'</span>:
+ <span class="variable">defer</span> = <span class="variable">true</span>;
+ <span class="variable">childProcess</span>.<span class="variable">exec</span>(<span class="string">'rm -fr lib-cov &amp;&amp; node-jscoverage lib lib-cov'</span>, <span class="keyword">function</span>(<span class="variable">err</span>){
+ <span class="keyword">if</span> (<span class="variable">err</span>) <span class="keyword">throw</span> <span class="variable">err</span>;
+ <span class="variable">require</span>.<span class="variable">paths</span>.<span class="variable">unshift</span>(<span class="string">'lib-cov'</span>);
+ <span class="variable">run</span>(<span class="variable">files</span>);
+ })
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-b'</span>:
+ <span class="keyword">case</span> <span class="string">'--boring'</span>:
+ <span class="variable">boring</span> = <span class="variable">true</span>;
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-w'</span>:
+ <span class="keyword">case</span> <span class="string">'--watch'</span>:
+ <span class="variable">watch</span> = <span class="variable">true</span>;
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-g'</span>:
+ <span class="keyword">case</span> <span class="string">'--growl'</span>:
+ <span class="variable">growl</span> = <span class="variable">true</span>;
+ <span class="keyword">break</span>;
+ <span class="keyword">case</span> <span class="string">'-s'</span>:
+ <span class="keyword">case</span> <span class="string">'--serial'</span>:
+ <span class="variable">serial</span> = <span class="variable">true</span>;
+ <span class="keyword">break</span>;
+ <span class="keyword">default</span>:
+ <span class="keyword">if</span> (<span class="regexp">/\.js$/</span>.<span class="variable">test</span>(<span class="variable">arg</span>)) {
+ <span class="variable">files</span>.<span class="variable">push</span>(<span class="variable">arg</span>);
+ }
+ <span class="keyword">break</span>;
+ }
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Colorized sys.error().</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> str</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">print</span>(<span class="variable">str</span>){
+ <span class="variable">sys</span>.<span class="variable">error</span>(<span class="variable">colorize</span>(<span class="variable">str</span>));
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Colorize the given string using ansi-escape sequences.
+Disabled when --boring is set.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> str</p></li><li><p><strong>return</strong>: <em>String</em> </p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">colorize</span>(<span class="variable">str</span>){
+ <span class="keyword">var</span> <span class="variable">colors</span> = { <span class="variable">bold</span>: <span class="number integer">1</span>, <span class="variable">red</span>: <span class="number integer">31</span>, <span class="variable">green</span>: <span class="number integer">32</span>, <span class="variable">yellow</span>: <span class="number integer">33</span> };
+ <span class="keyword">return</span> <span class="variable">str</span>.<span class="variable">replace</span>(<span class="regexp">/\[(\w+)\]\{([^]*?)\}/g</span>, <span class="keyword">function</span>(<span class="variable">_</span>, <span class="variable">color</span>, <span class="variable">str</span>){
+ <span class="keyword">return</span> <span class="variable">boring</span>
+ ? <span class="variable">str</span>
+ : <span class="string">'\x1B['</span> + <span class="variable">colors</span>[<span class="variable">color</span>] + <span class="string">'m'</span> + <span class="variable">str</span> + <span class="string">'\x1B[0m'</span>;
+ });
+}
+
+<span class="comment">// Alias deepEqual as eql for complex equality</span>
+
+<span class="variable">assert</span>.<span class="variable">eql</span> = <span class="variable">assert</span>.<span class="variable">deepEqual</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Assert that <code>val</code> is null.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Mixed</em> val</p></li><li><p><strong>param</strong>: <em>String</em> msg</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">assert</span>.<span class="variable">isNull</span> = <span class="keyword">function</span>(<span class="variable">val</span>, <span class="variable">msg</span>) {
+ <span class="variable">assert</span>.<span class="variable">strictEqual</span>(<span class="keyword">null</span>, <span class="variable">val</span>, <span class="variable">msg</span>);
+};</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Assert that <code>val</code> is not null.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Mixed</em> val</p></li><li><p><strong>param</strong>: <em>String</em> msg</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">assert</span>.<span class="variable">isNotNull</span> = <span class="keyword">function</span>(<span class="variable">val</span>, <span class="variable">msg</span>) {
+ <span class="variable">assert</span>.<span class="variable">notStrictEqual</span>(<span class="keyword">null</span>, <span class="variable">val</span>, <span class="variable">msg</span>);
+};</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Assert that <code>val</code> is undefined.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Mixed</em> val</p></li><li><p><strong>param</strong>: <em>String</em> msg</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">assert</span>.<span class="variable">isUndefined</span> = <span class="keyword">function</span>(<span class="variable">val</span>, <span class="variable">msg</span>) {
+ <span class="variable">assert</span>.<span class="variable">strictEqual</span>(<span class="variable">undefined</span>, <span class="variable">val</span>, <span class="variable">msg</span>);
+};</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Assert that <code>val</code> is not undefined.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Mixed</em> val</p></li><li><p><strong>param</strong>: <em>String</em> msg</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">assert</span>.<span class="variable">isDefined</span> = <span class="keyword">function</span>(<span class="variable">val</span>, <span class="variable">msg</span>) {
+ <span class="variable">assert</span>.<span class="variable">notStrictEqual</span>(<span class="variable">undefined</span>, <span class="variable">val</span>, <span class="variable">msg</span>);
+};</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Assert that <code>obj</code> is <code>type</code>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Mixed</em> obj</p></li><li><p><strong>param</strong>: <em>String</em> type</p></li><li><p><strong>api</strong>: <em>public</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">assert</span>.<span class="variable">type</span> = <span class="keyword">function</span>(<span class="variable">obj</span>, <span class="variable">type</span>, <span class="variable">msg</span>){
+ <span class="keyword">var</span> <span class="variable">real</span> = <span class="keyword">typeof</span> <span class="variable">obj</span>;
+ <span class="variable">msg</span> = <span class="variable">msg</span> || <span class="string">'typeof '</span> + <span class="variable">sys</span>.<span class="variable">inspect</span>(<span class="variable">obj</span>) + <span class="string">' is '</span> + <span class="variable">real</span> + <span class="string">', expected '</span> + <span class="variable">type</span>;
+ <span class="variable">assert</span>.<span class="variable">ok</span>(<span class="variable">type</span> === <span class="variable">real</span>, <span class="variable">msg</span>);
+};</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Assert that <code>str</code> matches <code>regexp</code>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> str</p></li><li><p><strong>param</strong>: <em>RegExp</em> regexp</p></li><li><p><strong>param</strong>: <em>String</em> msg</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">assert</span>.<span class="variable">match</span> = <span class="keyword">function</span>(<span class="variable">str</span>, <span class="variable">regexp</span>, <span class="variable">msg</span>) {
+ <span class="variable">msg</span> = <span class="variable">msg</span> || <span class="variable">sys</span>.<span class="variable">inspect</span>(<span class="variable">str</span>) + <span class="string">' does not match '</span> + <span class="variable">sys</span>.<span class="variable">inspect</span>(<span class="variable">regexp</span>);
+ <span class="variable">assert</span>.<span class="variable">ok</span>(<span class="variable">regexp</span>.<span class="variable">test</span>(<span class="variable">str</span>), <span class="variable">msg</span>);
+};</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Assert that <code>val</code> is within <code>obj</code>.</p>
+
+<h2>Examples</h2>
+
+<p> assert.includes('foobar', 'bar');
+ assert.includes(['foo', 'bar'], 'foo');</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String | Array</em> obj</p></li><li><p><strong>param</strong>: <em>Mixed</em> val</p></li><li><p><strong>param</strong>: <em>String</em> msg</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">assert</span>.<span class="variable">includes</span> = <span class="keyword">function</span>(<span class="variable">obj</span>, <span class="variable">val</span>, <span class="variable">msg</span>) {
+ <span class="variable">msg</span> = <span class="variable">msg</span> || <span class="variable">sys</span>.<span class="variable">inspect</span>(<span class="variable">obj</span>) + <span class="string">' does not include '</span> + <span class="variable">sys</span>.<span class="variable">inspect</span>(<span class="variable">val</span>);
+ <span class="variable">assert</span>.<span class="variable">ok</span>(<span class="variable">obj</span>.<span class="variable">indexOf</span>(<span class="variable">val</span>) &<span class="variable">gt</span>;= <span class="number integer">0</span>, <span class="variable">msg</span>);
+};</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Assert length of <code>val</code> is <code>n</code>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Mixed</em> val</p></li><li><p><strong>param</strong>: <em>Number</em> n</p></li><li><p><strong>param</strong>: <em>String</em> msg</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">assert</span>.<span class="variable">length</span> = <span class="keyword">function</span>(<span class="variable">val</span>, <span class="variable">n</span>, <span class="variable">msg</span>) {
+ <span class="variable">msg</span> = <span class="variable">msg</span> || <span class="variable">sys</span>.<span class="variable">inspect</span>(<span class="variable">val</span>) + <span class="string">' has length of '</span> + <span class="variable">val</span>.<span class="variable">length</span> + <span class="string">', expected '</span> + <span class="variable">n</span>;
+ <span class="variable">assert</span>.<span class="variable">equal</span>(<span class="variable">n</span>, <span class="variable">val</span>.<span class="variable">length</span>, <span class="variable">msg</span>);
+};</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Assert response from <code>server</code> with
+the given <code>req</code> object and <code>res</code> assertions object.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Server</em> server</p></li><li><p><strong>param</strong>: <em>Object</em> req</p></li><li><p><strong>param</strong>: <em>Object | Function</em> res</p></li><li><p><strong>param</strong>: <em>String</em> msg</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">assert</span>.<span class="variable">response</span> = <span class="keyword">function</span>(<span class="variable">server</span>, <span class="variable">req</span>, <span class="variable">res</span>, <span class="variable">msg</span>){
+ <span class="comment">// Callback as third or fourth arg</span>
+ <span class="keyword">var</span> <span class="variable">callback</span> = <span class="keyword">typeof</span> <span class="variable">res</span> === <span class="string">'function'</span>
+ ? <span class="variable">res</span>
+ : <span class="keyword">typeof</span> <span class="variable">msg</span> === <span class="string">'function'</span>
+ ? <span class="variable">msg</span>
+ : <span class="keyword">function</span>(){};
+
+ <span class="comment">// Default messate to test title</span>
+ <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="variable">msg</span> === <span class="string">'function'</span>) <span class="variable">msg</span> = <span class="keyword">null</span>;
+ <span class="variable">msg</span> = <span class="variable">msg</span> || <span class="variable">assert</span>.<span class="variable">testTitle</span>;
+ <span class="variable">msg</span> += <span class="string">'. '</span>;
+
+ <span class="comment">// Pending responses</span>
+ <span class="variable">server</span>.<span class="variable">__pending</span> = <span class="variable">server</span>.<span class="variable">__pending</span> || <span class="number integer">0</span>;
+ <span class="variable">server</span>.<span class="variable">__pending</span>++;
+
+ <span class="comment">// Create client</span>
+ <span class="keyword">if</span> (!<span class="variable">server</span>.<span class="variable">fd</span>) {
+ <span class="variable">server</span>.<span class="variable">listen</span>(<span class="variable">server</span>.<span class="variable">__port</span> = <span class="variable">port</span>++, <span class="string">'127.0.0.1'</span>);
+ <span class="variable">server</span>.<span class="variable">client</span> = <span class="variable">http</span>.<span class="variable">createClient</span>(<span class="variable">server</span>.<span class="variable">__port</span>);
+ }
+
+ <span class="comment">// Issue request</span>
+ <span class="keyword">var</span> <span class="variable">timer</span>,
+ <span class="variable">client</span> = <span class="variable">server</span>.<span class="variable">client</span>,
+ <span class="variable">method</span> = <span class="variable">req</span>.<span class="variable">method</span> || <span class="string">'GET'</span>,
+ <span class="variable">status</span> = <span class="variable">res</span>.<span class="variable">status</span> || <span class="variable">res</span>.<span class="variable">statusCode</span>,
+ <span class="variable">data</span> = <span class="variable">req</span>.<span class="variable">data</span> || <span class="variable">req</span>.<span class="variable">body</span>,
+ <span class="variable">requestTimeout</span> = <span class="variable">req</span>.<span class="variable">timeout</span> || <span class="number integer">0</span>;
+
+ <span class="keyword">var</span> <span class="variable">request</span> = <span class="variable">client</span>.<span class="variable">request</span>(<span class="variable">method</span>, <span class="variable">req</span>.<span class="variable">url</span>, <span class="variable">req</span>.<span class="variable">headers</span>);
+
+ <span class="comment">// Timeout</span>
+ <span class="keyword">if</span> (<span class="variable">requestTimeout</span>) {
+ <span class="variable">timer</span> = <span class="variable">setTimeout</span>(<span class="keyword">function</span>(){
+ --<span class="variable">server</span>.<span class="variable">__pending</span> || <span class="variable">server</span>.<span class="variable">close</span>();
+ <span class="keyword">delete</span> <span class="variable">req</span>.<span class="variable">timeout</span>;
+ <span class="variable">assert</span>.<span class="variable">fail</span>(<span class="variable">msg</span> + <span class="string">'Request timed out after '</span> + <span class="variable">requestTimeout</span> + <span class="string">'ms.'</span>);
+ }, <span class="variable">requestTimeout</span>);
+ }
+
+ <span class="keyword">if</span> (<span class="variable">data</span>) <span class="variable">request</span>.<span class="variable">write</span>(<span class="variable">data</span>);
+ <span class="variable">request</span>.<span class="variable">addListener</span>(<span class="string">'response'</span>, <span class="keyword">function</span>(<span class="variable">response</span>){
+ <span class="variable">response</span>.<span class="variable">body</span> = <span class="string">''</span>;
+ <span class="variable">response</span>.<span class="variable">setEncoding</span>(<span class="string">'utf8'</span>);
+ <span class="variable">response</span>.<span class="variable">addListener</span>(<span class="string">'data'</span>, <span class="keyword">function</span>(<span class="variable">chunk</span>){ <span class="variable">response</span>.<span class="variable">body</span> += <span class="variable">chunk</span>; });
+ <span class="variable">response</span>.<span class="variable">addListener</span>(<span class="string">'end'</span>, <span class="keyword">function</span>(){
+ --<span class="variable">server</span>.<span class="variable">__pending</span> || <span class="variable">server</span>.<span class="variable">close</span>();
+ <span class="keyword">if</span> (<span class="variable">timer</span>) <span class="variable">clearTimeout</span>(<span class="variable">timer</span>);
+
+ <span class="comment">// Assert response body</span>
+ <span class="keyword">if</span> (<span class="variable">res</span>.<span class="variable">body</span> !== <span class="variable">undefined</span>) {
+ <span class="keyword">var</span> <span class="variable">eql</span> = <span class="variable">res</span>.<span class="variable">body</span> <span class="variable">instanceof</span> <span class="class">RegExp</span>
+ ? <span class="variable">res</span>.<span class="variable">body</span>.<span class="variable">test</span>(<span class="variable">response</span>.<span class="variable">body</span>)
+ : <span class="variable">res</span>.<span class="variable">body</span> === <span class="variable">response</span>.<span class="variable">body</span>;
+ <span class="variable">assert</span>.<span class="variable">ok</span>(
+ <span class="variable">eql</span>,
+ <span class="variable">msg</span> + <span class="string">'Invalid response body.\n'</span>
+ + <span class="string">' Expected: '</span> + <span class="variable">sys</span>.<span class="variable">inspect</span>(<span class="variable">res</span>.<span class="variable">body</span>) + <span class="string">'\n'</span>
+ + <span class="string">' Got: '</span> + <span class="variable">sys</span>.<span class="variable">inspect</span>(<span class="variable">response</span>.<span class="variable">body</span>)
+ );
+ }
+
+ <span class="comment">// Assert response status</span>
+ <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="variable">status</span> === <span class="string">'number'</span>) {
+ <span class="variable">assert</span>.<span class="variable">equal</span>(
+ <span class="variable">response</span>.<span class="variable">statusCode</span>,
+ <span class="variable">status</span>,
+ <span class="variable">msg</span> + <span class="variable">colorize</span>(<span class="string">'Invalid response status code.\n'</span>
+ + <span class="string">' Expected: [green]{'</span> + <span class="variable">status</span> + <span class="string">'}\n'</span>
+ + <span class="string">' Got: [red]{'</span> + <span class="variable">response</span>.<span class="variable">statusCode</span> + <span class="string">'}'</span>)
+ );
+ }
+
+ <span class="comment">// Assert response headers</span>
+ <span class="keyword">if</span> (<span class="variable">res</span>.<span class="variable">headers</span>) {
+ <span class="keyword">var</span> <span class="variable">keys</span> = <span class="class">Object</span>.<span class="variable">keys</span>(<span class="variable">res</span>.<span class="variable">headers</span>);
+ <span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">i</span> = <span class="number integer">0</span>, <span class="variable">len</span> = <span class="variable">keys</span>.<span class="variable">length</span>; <span class="variable">i</span> &<span class="variable">lt</span>; <span class="variable">len</span>; ++<span class="variable">i</span>) {
+ <span class="keyword">var</span> <span class="variable">name</span> = <span class="variable">keys</span>[<span class="variable">i</span>],
+ <span class="variable">actual</span> = <span class="variable">response</span>.<span class="variable">headers</span>[<span class="variable">name</span>.<span class="variable">toLowerCase</span>()],
+ <span class="variable">expected</span> = <span class="variable">res</span>.<span class="variable">headers</span>[<span class="variable">name</span>],
+ <span class="variable">eql</span> = <span class="variable">expected</span> <span class="variable">instanceof</span> <span class="class">RegExp</span>
+ ? <span class="variable">expected</span>.<span class="variable">test</span>(<span class="variable">actual</span>)
+ : <span class="variable">expected</span> == <span class="variable">actual</span>;
+ <span class="variable">assert</span>.<span class="variable">ok</span>(
+ <span class="variable">eql</span>,
+ <span class="variable">msg</span> + <span class="variable">colorize</span>(<span class="string">'Invalid response header [bold]{'</span> + <span class="variable">name</span> + <span class="string">'}.\n'</span>
+ + <span class="string">' Expected: [green]{'</span> + <span class="variable">expected</span> + <span class="string">'}\n'</span>
+ + <span class="string">' Got: [red]{'</span> + <span class="variable">actual</span> + <span class="string">'}'</span>)
+ );
+ }
+ }
+
+ <span class="comment">// Callback</span>
+ <span class="variable">callback</span>(<span class="variable">response</span>);
+ });
+ });
+ <span class="variable">request</span>.<span class="variable">end</span>();
+};</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Pad the given string to the maximum width provided.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> str</p></li><li><p><strong>param</strong>: <em>Number</em> width</p></li><li><p><strong>return</strong>: <em>String</em> </p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">lpad</span>(<span class="variable">str</span>, <span class="variable">width</span>) {
+ <span class="variable">str</span> = <span class="class">String</span>(<span class="variable">str</span>);
+ <span class="keyword">var</span> <span class="variable">n</span> = <span class="variable">width</span> - <span class="variable">str</span>.<span class="variable">length</span>;
+ <span class="keyword">if</span> (<span class="variable">n</span> &<span class="variable">lt</span>; <span class="number integer">1</span>) <span class="keyword">return</span> <span class="variable">str</span>;
+ <span class="keyword">while</span> (<span class="variable">n</span>--) <span class="variable">str</span> = <span class="string">' '</span> + <span class="variable">str</span>;
+ <span class="keyword">return</span> <span class="variable">str</span>;
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Pad the given string to the maximum width provided.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> str</p></li><li><p><strong>param</strong>: <em>Number</em> width</p></li><li><p><strong>return</strong>: <em>String</em> </p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">rpad</span>(<span class="variable">str</span>, <span class="variable">width</span>) {
+ <span class="variable">str</span> = <span class="class">String</span>(<span class="variable">str</span>);
+ <span class="keyword">var</span> <span class="variable">n</span> = <span class="variable">width</span> - <span class="variable">str</span>.<span class="variable">length</span>;
+ <span class="keyword">if</span> (<span class="variable">n</span> &<span class="variable">lt</span>; <span class="number integer">1</span>) <span class="keyword">return</span> <span class="variable">str</span>;
+ <span class="keyword">while</span> (<span class="variable">n</span>--) <span class="variable">str</span> = <span class="variable">str</span> + <span class="string">' '</span>;
+ <span class="keyword">return</span> <span class="variable">str</span>;
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Report test coverage.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Object</em> cov</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">reportCoverage</span>(<span class="variable">cov</span>) {
+ <span class="variable">populateCoverage</span>(<span class="variable">cov</span>);
+ <span class="comment">// Stats</span>
+ <span class="variable">print</span>(<span class="string">'\n [bold]{Test Coverage}\n'</span>);
+ <span class="keyword">var</span> <span class="variable">sep</span> = <span class="string">' +------------------------------------------+----------+------+------+--------+'</span>,
+ <span class="variable">lastSep</span> = <span class="string">' +----------+------+------+--------+'</span>;
+ <span class="variable">sys</span>.<span class="variable">puts</span>(<span class="variable">sep</span>);
+ <span class="variable">sys</span>.<span class="variable">puts</span>(<span class="string">' | filename | coverage | LOC | SLOC | missed |'</span>);
+ <span class="variable">sys</span>.<span class="variable">puts</span>(<span class="variable">sep</span>);
+ <span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">name</span> <span class="keyword">in</span> <span class="variable">cov</span>) {
+ <span class="keyword">var</span> <span class="variable">file</span> = <span class="variable">cov</span>[<span class="variable">name</span>];
+ <span class="keyword">if</span> (<span class="class">Array</span>.<span class="variable">isArray</span>(<span class="variable">file</span>)) {
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' | '</span> + <span class="variable">rpad</span>(<span class="variable">name</span>, <span class="number integer">40</span>));
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' | '</span> + <span class="variable">lpad</span>(<span class="variable">file</span>.<span class="variable">coverage</span>.<span class="variable">toFixed</span>(<span class="number integer">2</span>), <span class="number integer">8</span>));
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' | '</span> + <span class="variable">lpad</span>(<span class="variable">file</span>.<span class="class">LOC</span>, <span class="number integer">4</span>));
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' | '</span> + <span class="variable">lpad</span>(<span class="variable">file</span>.<span class="class">SLOC</span>, <span class="number integer">4</span>));
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' | '</span> + <span class="variable">lpad</span>(<span class="variable">file</span>.<span class="variable">totalMisses</span>, <span class="number integer">6</span>));
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' |\n'</span>);
+ }
+ }
+ <span class="variable">sys</span>.<span class="variable">puts</span>(<span class="variable">sep</span>);
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' '</span> + <span class="variable">rpad</span>(<span class="string">''</span>, <span class="number integer">40</span>));
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' | '</span> + <span class="variable">lpad</span>(<span class="variable">cov</span>.<span class="variable">coverage</span>.<span class="variable">toFixed</span>(<span class="number integer">2</span>), <span class="number integer">8</span>));
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' | '</span> + <span class="variable">lpad</span>(<span class="variable">cov</span>.<span class="class">LOC</span>, <span class="number integer">4</span>));
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' | '</span> + <span class="variable">lpad</span>(<span class="variable">cov</span>.<span class="class">SLOC</span>, <span class="number integer">4</span>));
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' | '</span> + <span class="variable">lpad</span>(<span class="variable">cov</span>.<span class="variable">totalMisses</span>, <span class="number integer">6</span>));
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">' |\n'</span>);
+ <span class="variable">sys</span>.<span class="variable">puts</span>(<span class="variable">lastSep</span>);
+ <span class="comment">// Source</span>
+ <span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">name</span> <span class="keyword">in</span> <span class="variable">cov</span>) {
+ <span class="keyword">if</span> (<span class="variable">name</span>.<span class="variable">match</span>(<span class="regexp">/\.js$/</span>)) {
+ <span class="keyword">var</span> <span class="variable">file</span> = <span class="variable">cov</span>[<span class="variable">name</span>];
+ <span class="variable">print</span>(<span class="string">'\n [bold]{'</span> + <span class="variable">name</span> + <span class="string">'}:'</span>);
+ <span class="variable">print</span>(<span class="variable">file</span>.<span class="variable">source</span>);
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">'\n'</span>);
+ }
+ }
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Populate code coverage data.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Object</em> cov</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">populateCoverage</span>(<span class="variable">cov</span>) {
+ <span class="variable">cov</span>.<span class="class">LOC</span> =
+ <span class="variable">cov</span>.<span class="class">SLOC</span> =
+ <span class="variable">cov</span>.<span class="variable">totalFiles</span> =
+ <span class="variable">cov</span>.<span class="variable">totalHits</span> =
+ <span class="variable">cov</span>.<span class="variable">totalMisses</span> =
+ <span class="variable">cov</span>.<span class="variable">coverage</span> = <span class="number integer">0</span>;
+ <span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">name</span> <span class="keyword">in</span> <span class="variable">cov</span>) {
+ <span class="keyword">var</span> <span class="variable">file</span> = <span class="variable">cov</span>[<span class="variable">name</span>];
+ <span class="keyword">if</span> (<span class="class">Array</span>.<span class="variable">isArray</span>(<span class="variable">file</span>)) {
+ <span class="comment">// Stats</span>
+ ++<span class="variable">cov</span>.<span class="variable">totalFiles</span>;
+ <span class="variable">cov</span>.<span class="variable">totalHits</span> += <span class="variable">file</span>.<span class="variable">totalHits</span> = <span class="variable">coverage</span>(<span class="variable">file</span>, <span class="variable">true</span>);
+ <span class="variable">cov</span>.<span class="variable">totalMisses</span> += <span class="variable">file</span>.<span class="variable">totalMisses</span> = <span class="variable">coverage</span>(<span class="variable">file</span>, <span class="variable">false</span>);
+ <span class="variable">file</span>.<span class="variable">totalLines</span> = <span class="variable">file</span>.<span class="variable">totalHits</span> + <span class="variable">file</span>.<span class="variable">totalMisses</span>;
+ <span class="variable">cov</span>.<span class="class">SLOC</span> += <span class="variable">file</span>.<span class="class">SLOC</span> = <span class="variable">file</span>.<span class="variable">totalLines</span>;
+ <span class="keyword">if</span> (!<span class="variable">file</span>.<span class="variable">source</span>) <span class="variable">file</span>.<span class="variable">source</span> = [];
+ <span class="variable">cov</span>.<span class="class">LOC</span> += <span class="variable">file</span>.<span class="class">LOC</span> = <span class="variable">file</span>.<span class="variable">source</span>.<span class="variable">length</span>;
+ <span class="variable">file</span>.<span class="variable">coverage</span> = (<span class="variable">file</span>.<span class="variable">totalHits</span> / <span class="variable">file</span>.<span class="variable">totalLines</span>) * <span class="number integer">100</span>;
+ <span class="comment">// Source</span>
+ <span class="keyword">var</span> <span class="variable">width</span> = <span class="variable">file</span>.<span class="variable">source</span>.<span class="variable">length</span>.<span class="variable">toString</span>().<span class="variable">length</span>;
+ <span class="variable">file</span>.<span class="variable">source</span> = <span class="variable">file</span>.<span class="variable">source</span>.<span class="variable">map</span>(<span class="keyword">function</span>(<span class="variable">line</span>, <span class="variable">i</span>){
+ ++<span class="variable">i</span>;
+ <span class="keyword">var</span> <span class="variable">hits</span> = <span class="variable">file</span>[<span class="variable">i</span>] === <span class="number integer">0</span> ? <span class="number integer">0</span> : (<span class="variable">file</span>[<span class="variable">i</span>] || <span class="string">' '</span>);
+ <span class="keyword">if</span> (!<span class="variable">boring</span>) {
+ <span class="keyword">if</span> (<span class="variable">hits</span> === <span class="number integer">0</span>) {
+ <span class="variable">hits</span> = <span class="string">'\x1b[31m'</span> + <span class="variable">hits</span> + <span class="string">'\x1b[0m'</span>;
+ <span class="variable">line</span> = <span class="string">'\x1b[41m'</span> + <span class="variable">line</span> + <span class="string">'\x1b[0m'</span>;
+ } <span class="keyword">else</span> {
+ <span class="variable">hits</span> = <span class="string">'\x1b[32m'</span> + <span class="variable">hits</span> + <span class="string">'\x1b[0m'</span>;
+ }
+ }
+ <span class="keyword">return</span> <span class="string">'\n '</span> + <span class="variable">lpad</span>(<span class="variable">i</span>, <span class="variable">width</span>) + <span class="string">' | '</span> + <span class="variable">hits</span> + <span class="string">' | '</span> + <span class="variable">line</span>;
+ }).<span class="variable">join</span>(<span class="string">''</span>);
+ }
+ }
+ <span class="variable">cov</span>.<span class="variable">coverage</span> = (<span class="variable">cov</span>.<span class="variable">totalHits</span> / <span class="variable">cov</span>.<span class="class">SLOC</span>) * <span class="number integer">100</span>;
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Total coverage for the given file data.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Array</em> data</p></li><li><p><strong>return</strong>: <em>Type</em> </p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">coverage</span>(<span class="variable">data</span>, <span class="variable">val</span>) {
+ <span class="keyword">var</span> <span class="variable">n</span> = <span class="number integer">0</span>;
+ <span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">i</span> = <span class="number integer">0</span>, <span class="variable">len</span> = <span class="variable">data</span>.<span class="variable">length</span>; <span class="variable">i</span> &<span class="variable">lt</span>; <span class="variable">len</span>; ++<span class="variable">i</span>) {
+ <span class="keyword">if</span> (<span class="variable">data</span>[<span class="variable">i</span>] !== <span class="variable">undefined</span> &<span class="variable">amp</span>;&<span class="variable">amp</span>; <span class="variable">data</span>[<span class="variable">i</span>] == <span class="variable">val</span>) ++<span class="variable">n</span>;
+ }
+ <span class="keyword">return</span> <span class="variable">n</span>;
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Run the given test <code>files</code>, or try <em>test/*</em>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Array</em> files</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">run</span>(<span class="variable">files</span>) {
+ <span class="keyword">if</span> (!<span class="variable">files</span>.<span class="variable">length</span>) {
+ <span class="keyword">try</span> {
+ <span class="variable">files</span> = <span class="variable">fs</span>.<span class="variable">readdirSync</span>(<span class="string">'test'</span>).<span class="variable">map</span>(<span class="keyword">function</span>(<span class="variable">file</span>){
+ <span class="keyword">return</span> <span class="string">'test/'</span> + <span class="variable">file</span>;
+ });
+ } <span class="keyword">catch</span> (<span class="variable">err</span>) {
+ <span class="variable">print</span>(<span class="string">'\n failed to load tests in [bold]{./test}\n'</span>);
+ ++<span class="variable">failures</span>;
+ <span class="variable">process</span>.<span class="variable">exit</span>(<span class="number integer">1</span>);
+ }
+ }
+ <span class="keyword">if</span> (<span class="variable">watch</span>) <span class="variable">watchFiles</span>(<span class="variable">files</span>);
+ <span class="variable">runFiles</span>(<span class="variable">files</span>);
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Show the cursor when <code>show</code> is true, otherwise hide it.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Boolean</em> show</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">cursor</span>(<span class="variable">show</span>) {
+ <span class="keyword">if</span> (<span class="variable">show</span>) {
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">'\x1b[?25h'</span>);
+ } <span class="keyword">else</span> {
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">'\x1b[?25l'</span>);
+ }
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Run the given test <code>files</code>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Array</em> files</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">runFiles</span>(<span class="variable">files</span>) {
+ <span class="keyword">if</span> (<span class="variable">serial</span>) {
+ (<span class="keyword">function</span> <span class="variable">next</span>(){
+ <span class="keyword">if</span> (<span class="variable">files</span>.<span class="variable">length</span>) {
+ <span class="variable">runFile</span>(<span class="variable">files</span>.<span class="variable">shift</span>(), <span class="variable">next</span>);
+ }
+ })();
+ } <span class="keyword">else</span> {
+ <span class="variable">files</span>.<span class="variable">forEach</span>(<span class="variable">runFile</span>);
+ }
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Run tests for the given <code>file</code>, callback <code>fn()</code> when finished.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> file</p></li><li><p><strong>param</strong>: <em>Function</em> fn</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">runFile</span>(<span class="variable">file</span>, <span class="variable">fn</span>) {
+ <span class="keyword">if</span> (<span class="variable">file</span>.<span class="variable">match</span>(<span class="regexp">/\.js$/</span>)) {
+ <span class="keyword">var</span> <span class="variable">title</span> = <span class="variable">path</span>.<span class="variable">basename</span>(<span class="variable">file</span>),
+ <span class="variable">file</span> = <span class="variable">path</span>.<span class="variable">join</span>(<span class="variable">cwd</span>, <span class="variable">file</span>),
+ <span class="variable">mod</span> = <span class="variable">require</span>(<span class="variable">file</span>.<span class="variable">replace</span>(<span class="regexp">/\.js$/</span>, <span class="string">''</span>));
+ (<span class="keyword">function</span> <span class="variable">check</span>(){
+ <span class="keyword">var</span> <span class="variable">len</span> = <span class="class">Object</span>.<span class="variable">keys</span>(<span class="variable">mod</span>).<span class="variable">length</span>;
+ <span class="keyword">if</span> (<span class="variable">len</span>) {
+ <span class="variable">runSuite</span>(<span class="variable">title</span>, <span class="variable">mod</span>, <span class="variable">fn</span>);
+ } <span class="keyword">else</span> {
+ <span class="variable">setTimeout</span>(<span class="variable">check</span>, <span class="number integer">20</span>);
+ }
+ })();
+ }
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Clear the module cache for the given <code>file</code>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> file</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">clearCache</span>(<span class="variable">file</span>) {
+ <span class="keyword">var</span> <span class="variable">keys</span> = <span class="class">Object</span>.<span class="variable">keys</span>(<span class="variable">module</span>.<span class="variable">moduleCache</span>);
+ <span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">i</span> = <span class="number integer">0</span>, <span class="variable">len</span> = <span class="variable">keys</span>.<span class="variable">length</span>; <span class="variable">i</span> &<span class="variable">lt</span>; <span class="variable">len</span>; ++<span class="variable">i</span>) {
+ <span class="keyword">var</span> <span class="variable">key</span> = <span class="variable">keys</span>[<span class="variable">i</span>];
+ <span class="keyword">if</span> (<span class="variable">key</span>.<span class="variable">indexOf</span>(<span class="variable">file</span>) === <span class="variable">key</span>.<span class="variable">length</span> - <span class="variable">file</span>.<span class="variable">length</span>) {
+ <span class="keyword">delete</span> <span class="variable">module</span>.<span class="variable">moduleCache</span>[<span class="variable">key</span>];
+ }
+ }
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Watch the given <code>files</code> for changes.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Array</em> files</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">watchFiles</span>(<span class="variable">files</span>) {
+ <span class="keyword">var</span> <span class="variable">p</span> = <span class="number integer">0</span>,
+ <span class="variable">c</span> = [<span class="string">'▫ '</span>, <span class="string">'▫▫ '</span>, <span class="string">'▫▫▫ '</span>, <span class="string">' ▫▫▫'</span>,
+ <span class="string">' ▫▫'</span>, <span class="string">' ▫'</span>, <span class="string">' ▫'</span>, <span class="string">' ▫▫'</span>,
+ <span class="string">'▫▫▫ '</span>, <span class="string">'▫▫ '</span>, <span class="string">'▫ '</span>],
+ <span class="variable">l</span> = <span class="variable">c</span>.<span class="variable">length</span>;
+ <span class="variable">cursor</span>(<span class="variable">false</span>);
+ <span class="variable">setInterval</span>(<span class="keyword">function</span>(){
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="variable">colorize</span>(<span class="string">' [green]{'</span> + <span class="variable">c</span>[<span class="variable">p</span>++ % <span class="variable">l</span>] + <span class="string">'} watching\r'</span>));
+ }, <span class="number integer">100</span>);
+ <span class="variable">files</span>.<span class="variable">forEach</span>(<span class="keyword">function</span>(<span class="variable">file</span>){
+ <span class="variable">fs</span>.<span class="variable">watchFile</span>(<span class="variable">file</span>, { <span class="variable">interval</span>: <span class="number integer">100</span> }, <span class="keyword">function</span>(<span class="variable">curr</span>, <span class="variable">prev</span>){
+ <span class="keyword">if</span> (<span class="variable">curr</span>.<span class="variable">mtime</span> &<span class="variable">gt</span>; <span class="variable">prev</span>.<span class="variable">mtime</span>) {
+ <span class="variable">print</span>(<span class="string">' [yellow]{◦} '</span> + <span class="variable">file</span>);
+ <span class="variable">clearCache</span>(<span class="variable">file</span>);
+ <span class="variable">runFile</span>(<span class="variable">file</span>);
+ }
+ });
+ });
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Report <code>err</code> for the given <code>test</code> and <code>suite</code>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> suite</p></li><li><p><strong>param</strong>: <em>String</em> test</p></li><li><p><strong>param</strong>: <em>Error</em> err</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">error</span>(<span class="variable">suite</span>, <span class="variable">test</span>, <span class="variable">err</span>) {
+ ++<span class="variable">failures</span>;
+ <span class="keyword">var</span> <span class="variable">name</span> = <span class="variable">err</span>.<span class="variable">name</span>,
+ <span class="variable">stack</span> = <span class="variable">err</span>.<span class="variable">stack</span>.<span class="variable">replace</span>(<span class="variable">err</span>.<span class="variable">name</span>, <span class="string">''</span>),
+ <span class="keyword">label</span> = <span class="variable">test</span> === <span class="string">'uncaught'</span>
+ ? <span class="variable">test</span>
+ : <span class="variable">suite</span> + <span class="string">' '</span> + <span class="variable">test</span>;
+ <span class="variable">print</span>(<span class="string">'\n [bold]{'</span> + <span class="keyword">label</span> + <span class="string">'}: [red]{'</span> + <span class="variable">name</span> + <span class="string">'}'</span> + <span class="variable">stack</span> + <span class="string">'\n'</span>);
+ <span class="keyword">if</span> (<span class="variable">watch</span>) <span class="variable">notify</span>(<span class="keyword">label</span> + <span class="string">' failed'</span>);
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Run the given tests, callback <code>fn()</code> when finished.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> title</p></li><li><p><strong>param</strong>: <em>Object</em> tests</p></li><li><p><strong>param</strong>: <em>Function</em> fn</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="variable">dots</span> = <span class="number integer">0</span>;
+<span class="keyword">function</span> <span class="variable">runSuite</span>(<span class="variable">title</span>, <span class="variable">tests</span>, <span class="variable">fn</span>) {
+ <span class="comment">// Keys</span>
+ <span class="keyword">var</span> <span class="variable">keys</span> = <span class="variable">only</span>.<span class="variable">length</span>
+ ? <span class="variable">only</span>.<span class="variable">slice</span>(<span class="number integer">0</span>)
+ : <span class="class">Object</span>.<span class="variable">keys</span>(<span class="variable">tests</span>);
+
+ <span class="comment">// Setup</span>
+ <span class="keyword">var</span> <span class="variable">setup</span> = <span class="variable">tests</span>.<span class="variable">setup</span> || <span class="keyword">function</span>(<span class="variable">fn</span>){ <span class="variable">fn</span>(); };
+
+ <span class="comment">// Iterate tests</span>
+ (<span class="keyword">function</span> <span class="variable">next</span>(){
+ <span class="keyword">if</span> (<span class="variable">keys</span>.<span class="variable">length</span>) {
+ <span class="keyword">var</span> <span class="variable">key</span>,
+ <span class="variable">test</span> = <span class="variable">tests</span>[<span class="variable">key</span> = <span class="variable">keys</span>.<span class="variable">shift</span>()];
+ <span class="comment">// Non-tests</span>
+ <span class="keyword">if</span> (<span class="variable">key</span> === <span class="string">'setup'</span>) <span class="keyword">return</span> <span class="variable">next</span>();
+
+ <span class="comment">// Run test</span>
+ <span class="keyword">if</span> (<span class="variable">test</span>) {
+ <span class="keyword">try</span> {
+ ++<span class="variable">testcount</span>;
+ <span class="variable">assert</span>.<span class="variable">testTitle</span> = <span class="variable">key</span>;
+ <span class="keyword">if</span> (<span class="variable">serial</span>) {
+ <span class="keyword">if</span> (!<span class="variable">watch</span>) {
+ <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">'.'</span>);
+ <span class="keyword">if</span> (++<span class="variable">dots</span> % <span class="number integer">25</span> === <span class="number integer">0</span>) <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">'\n'</span>);
+ }
+ <span class="variable">setup</span>(<span class="keyword">function</span>(){
+ <span class="keyword">if</span> (<span class="variable">test</span>.<span class="variable">length</span> &<span class="variable">lt</span>; <span class="number integer">1</span>) {
+ <span class="variable">test</span>();
+ <span class="variable">next</span>();
+ } <span class="keyword">else</span> {
+ <span class="keyword">var</span> <span class="variable">id</span> = <span class="variable">setTimeout</span>(<span class="keyword">function</span>(){
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="class">Error</span>(&<span class="variable">quot</span>;<span class="string">'&quot; + key + &quot;'</span> <span class="variable">timed</span> <span class="variable">out</span>&<span class="variable">quot</span>;);
+ }, <span class="variable">timeout</span>);
+ <span class="variable">test</span>(<span class="keyword">function</span>(){
+ <span class="variable">clearTimeout</span>(<span class="variable">id</span>);
+ <span class="variable">next</span>();
+ });
+ }
+ });
+ } <span class="keyword">else</span> {
+ <span class="variable">test</span>(<span class="keyword">function</span>(<span class="variable">fn</span>){
+ <span class="variable">process</span>.<span class="variable">addListener</span>(<span class="string">'beforeExit'</span>, <span class="keyword">function</span>(){
+ <span class="keyword">try</span> {
+ <span class="variable">fn</span>();
+ } <span class="keyword">catch</span> (<span class="variable">err</span>) {
+ <span class="variable">error</span>(<span class="variable">title</span>, <span class="variable">key</span>, <span class="variable">err</span>);
+ }
+ });
+ });
+ }
+ } <span class="keyword">catch</span> (<span class="variable">err</span>) {
+ <span class="variable">error</span>(<span class="variable">title</span>, <span class="variable">key</span>, <span class="variable">err</span>);
+ }
+ }
+ <span class="keyword">if</span> (!<span class="variable">serial</span>) <span class="variable">next</span>();
+ } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable">serial</span>) {
+ <span class="variable">fn</span>();
+ }
+ })();
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Report exceptions.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">report</span>() {
+ <span class="variable">process</span>.<span class="variable">emit</span>(<span class="string">'beforeExit'</span>);
+ <span class="keyword">if</span> (<span class="variable">failures</span>) {
+ <span class="variable">print</span>(<span class="string">'\n [bold]{Failures}: [red]{'</span> + <span class="variable">failures</span> + <span class="string">'}\n\n'</span>);
+ <span class="variable">notify</span>(<span class="string">'Failures: '</span> + <span class="variable">failures</span>);
+ } <span class="keyword">else</span> {
+ <span class="keyword">if</span> (<span class="variable">serial</span>) <span class="variable">print</span>(<span class="string">''</span>);
+ <span class="variable">print</span>(<span class="string">'\n [green]{100%} '</span> + <span class="variable">testcount</span> + <span class="string">' tests\n'</span>);
+ <span class="variable">notify</span>(<span class="string">'100% ok'</span>);
+ }
+ <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="variable">_</span>$<span class="variable">jscoverage</span> === <span class="string">'object'</span>) {
+ <span class="variable">reportCoverage</span>(<span class="variable">_</span>$<span class="variable">jscoverage</span>);
+ }
+}</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Growl notify the given <code>msg</code>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> msg</p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">function</span> <span class="variable">notify</span>(<span class="variable">msg</span>) {
+ <span class="keyword">if</span> (<span class="variable">growl</span>) {
+ <span class="variable">childProcess</span>.<span class="variable">exec</span>(<span class="string">'growlnotify -name Expresso -m &quot;'</span> + <span class="variable">msg</span> + <span class="string">'&quot;'</span>);
+ }
+}
+
+<span class="comment">// Report uncaught exceptions</span>
+
+<span class="variable">process</span>.<span class="variable">addListener</span>(<span class="string">'uncaughtException'</span>, <span class="keyword">function</span>(<span class="variable">err</span>){
+ <span class="variable">error</span>(<span class="string">'uncaught'</span>, <span class="string">'uncaught'</span>, <span class="variable">err</span>);
+});
+
+<span class="comment">// Show cursor</span>
+
+[<span class="string">'INT'</span>, <span class="string">'TERM'</span>, <span class="string">'QUIT'</span>].<span class="variable">forEach</span>(<span class="keyword">function</span>(<span class="variable">sig</span>){
+ <span class="variable">process</span>.<span class="variable">addListener</span>(<span class="string">'SIG'</span> + <span class="variable">sig</span>, <span class="keyword">function</span>(){
+ <span class="variable">cursor</span>(<span class="variable">true</span>);
+ <span class="variable">process</span>.<span class="variable">exit</span>(<span class="number integer">1</span>);
+ });
+});
+
+<span class="comment">// Report test coverage when available</span>
+<span class="comment">// and emit &quot;beforeExit&quot; event to perform</span>
+<span class="comment">// final assertions</span>
+
+<span class="keyword">var</span> <span class="variable">orig</span> = <span class="variable">process</span>.<span class="variable">emit</span>;
+<span class="variable">process</span>.<span class="variable">emit</span> = <span class="keyword">function</span>(<span class="variable">event</span>){
+ <span class="keyword">if</span> (<span class="variable">event</span> === <span class="string">'exit'</span>) {
+ <span class="variable">report</span>();
+ <span class="variable">process</span>.<span class="variable">reallyExit</span>(<span class="variable">failures</span>);
+ }
+ <span class="variable">orig</span>.<span class="variable">apply</span>(<span class="this">this</span>, <span class="variable">arguments</span>);
+};
+
+<span class="comment">// Run test files</span>
+
+<span class="keyword">if</span> (!<span class="variable">defer</span>) <span class="variable">run</span>(<span class="variable">files</span>);
+</code></pre>
+</td>
+</tr> </body>
+</html></tbody></table> \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/docs/index.html b/node_modules/mongoose/support/expresso/docs/index.html
new file mode 100644
index 0000000..064313f
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/docs/index.html
@@ -0,0 +1,377 @@
+<html>
+ <head>
+ <title>Expresso - TDD Framework For Node</title>
+ <style>
+ body {
+ font: 13px/1.4 "Helvetica", "Lucida Grande", Arial, sans-serif;
+ text-align: center;
+ }
+ #ribbon {
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: 10;
+ }
+ #wrapper {
+ margin: 0 auto;
+ padding: 50px 80px;
+ width: 700px;
+ text-align: left;
+ }
+ h1, h2, h3 {
+ margin: 25px 0 15px 0;
+ }
+ h1 {
+ font-size: 35px;
+ }
+ pre {
+ margin: 0 5px;
+ padding: 15px;
+ border: 1px solid #eee;
+ }
+ a {
+ color: #00aaff;
+ }
+ </style>
+ </head>
+ <body>
+ <a href="http://github.com/visionmedia/expresso">
+ <img alt="Fork me on GitHub" id="ribbon" src="http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png" />
+ </a>
+ <div id="wrapper">
+ <h1>Expresso</h1>
+<div class='mp'>
+<h2 id="NAME">NAME</h2>
+<p class="man-name">
+ <code>index</code>
+</p>
+<p><a href="http://github.com/visionmedia/expresso">Expresso</a> is a JavaScript <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a> framework written for <a href="http://nodejs.org">nodejs</a>. Expresso is extremely fast, and is packed with features such as additional assertion methods, code coverage reporting, CI support, and more.</p>
+
+<h2 id="Features">Features</h2>
+
+<ul>
+<li>light-weight</li>
+<li>intuitive async support</li>
+<li>intuitive test runner executable</li>
+<li>test coverage support and reporting via <a href="http://github.com/visionmedia/node-jscoverage">node-jscoverage</a></li>
+<li>uses and extends the core <em>assert</em> module</li>
+<li><code>assert.eql()</code> alias of <code>assert.deepEqual()</code></li>
+<li><code>assert.response()</code> http response utility</li>
+<li><code>assert.includes()</code></li>
+<li><code>assert.isNull()</code></li>
+<li><code>assert.isUndefined()</code></li>
+<li><code>assert.isNotNull()</code></li>
+<li><code>assert.isDefined()</code></li>
+<li><code>assert.match()</code></li>
+<li><code>assert.length()</code></li>
+</ul>
+
+
+<h2 id="Installation">Installation</h2>
+
+<p>To install both expresso <em>and</em> node-jscoverage run
+the command below, which will first compile node-jscoverage:</p>
+
+<pre><code>$ make install
+</code></pre>
+
+<p>To install expresso alone without coverage reporting run:</p>
+
+<pre><code>$ make install-expresso
+</code></pre>
+
+<p>Install via npm:</p>
+
+<pre><code>$ npm install expresso
+</code></pre>
+
+<h2 id="Examples">Examples</h2>
+
+<p>To define tests we simply export several functions:</p>
+
+<pre><code>exports['test String#length'] = function(){
+ assert.equal(6, 'foobar'.length);
+};
+</code></pre>
+
+<p>Alternatively for large numbers of tests you may want to
+export your own object containing the tests, however this
+is essentially the as above:</p>
+
+<pre><code>module.exports = {
+ 'test String#length': function(){
+ assert.equal(6, 'foobar'.length);
+ }
+};
+</code></pre>
+
+<p>If you prefer not to use quoted keys:</p>
+
+<pre><code>exports.testsStringLength = function(){
+ assert.equal(6, 'foobar'.length);
+};
+</code></pre>
+
+<p>The argument passed to each callback is <em>beforeExit</em>,
+which is typically used to assert that callbacks have been
+invoked.</p>
+
+<pre><code>exports.testAsync = function(beforeExit){
+ var n = 0;
+ setTimeout(function(){
+ ++n;
+ assert.ok(true);
+ }, 200);
+ setTimeout(function(){
+ ++n;
+ assert.ok(true);
+ }, 200);
+ beforeExit(function(){
+ assert.equal(2, n, 'Ensure both timeouts are called');
+ });
+};
+</code></pre>
+
+<h2 id="Assert-Utilities">Assert Utilities</h2>
+
+<h3 id="assert-isNull-val-msg-">assert.isNull(val[, msg])</h3>
+
+<p>Asserts that the given <em>val</em> is <em>null</em>.</p>
+
+<pre><code>assert.isNull(null);
+</code></pre>
+
+<h3 id="assert-isNotNull-val-msg-">assert.isNotNull(val[, msg])</h3>
+
+<p>Asserts that the given <em>val</em> is not <em>null</em>.</p>
+
+<pre><code>assert.isNotNull(undefined);
+assert.isNotNull(false);
+</code></pre>
+
+<h3 id="assert-isUndefined-val-msg-">assert.isUndefined(val[, msg])</h3>
+
+<p>Asserts that the given <em>val</em> is <em>undefined</em>.</p>
+
+<pre><code>assert.isUndefined(undefined);
+</code></pre>
+
+<h3 id="assert-isDefined-val-msg-">assert.isDefined(val[, msg])</h3>
+
+<p>Asserts that the given <em>val</em> is not <em>undefined</em>.</p>
+
+<pre><code>assert.isDefined(null);
+assert.isDefined(false);
+</code></pre>
+
+<h3 id="assert-match-str-regexp-msg-">assert.match(str, regexp[, msg])</h3>
+
+<p>Asserts that the given <em>str</em> matches <em>regexp</em>.</p>
+
+<pre><code>assert.match('foobar', /^foo(bar)?/);
+assert.match('foo', /^foo(bar)?/);
+</code></pre>
+
+<h3 id="assert-length-val-n-msg-">assert.length(val, n[, msg])</h3>
+
+<p>Assert that the given <em>val</em> has a length of <em>n</em>.</p>
+
+<pre><code>assert.length([1,2,3], 3);
+assert.length('foo', 3);
+</code></pre>
+
+<h3 id="assert-type-obj-type-msg-">assert.type(obj, type[, msg])</h3>
+
+<p>Assert that the given <em>obj</em> is typeof <em>type</em>.</p>
+
+<pre><code>assert.type(3, 'number');
+</code></pre>
+
+<h3 id="assert-eql-a-b-msg-">assert.eql(a, b[, msg])</h3>
+
+<p>Assert that object <em>b</em> is equal to object <em>a</em>. This is an
+alias for the core <em>assert.deepEqual()</em> method which does complex
+comparisons, opposed to <em>assert.equal()</em> which uses <em>==</em>.</p>
+
+<pre><code>assert.eql('foo', 'foo');
+assert.eql([1,2], [1,2]);
+assert.eql({ foo: 'bar' }, { foo: 'bar' });
+</code></pre>
+
+<h3 id="assert-includes-obj-val-msg-">assert.includes(obj, val[, msg])</h3>
+
+<p>Assert that <em>obj</em> is within <em>val</em>. This method supports <em>Array_s
+and </em>Strings_s.</p>
+
+<pre><code>assert.includes([1,2,3], 3);
+assert.includes('foobar', 'foo');
+assert.includes('foobar', 'bar');
+</code></pre>
+
+<h3 id="assert-response-server-req-res-fn-msg-fn-">assert.response(server, req, res|fn[, msg|fn])</h3>
+
+<p>Performs assertions on the given <em>server</em>, which should <em>not</em> call
+listen(), as this is handled internally by expresso and the server
+is killed after all responses have completed. This method works with
+any <em>http.Server</em> instance, so <em>Connect</em> and <em>Express</em> servers will work
+as well.</p>
+
+<p>The <em>req</em> object may contain:</p>
+
+<ul>
+<li><em>url</em> request url</li>
+<li><em>timeout</em> timeout in milliseconds</li>
+<li><em>method</em> HTTP method</li>
+<li><em>data</em> request body</li>
+<li><em>headers</em> headers object</li>
+</ul>
+
+
+<p>The <em>res</em> object may be a callback function which
+receives the response for assertions, or an object
+which is then used to perform several assertions
+on the response with the following properties:</p>
+
+<ul>
+<li><em>body</em> assert response body (regexp or string)</li>
+<li><em>status</em> assert response status code</li>
+<li><em>header</em> assert that all given headers match (unspecified are ignored, use a regexp or string)</li>
+</ul>
+
+
+<p>When providing <em>res</em> you may then also pass a callback function
+as the fourth argument for additional assertions.</p>
+
+<p>Below are some examples:</p>
+
+<pre><code>assert.response(server, {
+ url: '/', timeout: 500
+}, {
+ body: 'foobar'
+});
+
+assert.response(server, {
+ url: '/',
+ method: 'GET'
+},{
+ body: '{"name":"tj"}',
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf8',
+ 'X-Foo': 'bar'
+ }
+});
+
+assert.response(server, {
+ url: '/foo',
+ method: 'POST',
+ data: 'bar baz'
+},{
+ body: '/foo bar baz',
+ status: 200
+}, 'Test POST');
+
+assert.response(server, {
+ url: '/foo',
+ method: 'POST',
+ data: 'bar baz'
+},{
+ body: '/foo bar baz',
+ status: 200
+}, function(res){
+ // All done, do some more tests if needed
+});
+
+assert.response(server, {
+ url: '/'
+}, function(res){
+ assert.ok(res.body.indexOf('tj') &gt;= 0, 'Test assert.response() callback');
+});
+</code></pre>
+
+<h2 id="expresso-1-">expresso(1)</h2>
+
+<p>To run a single test suite (file) run:</p>
+
+<pre><code>$ expresso test/a.test.js
+</code></pre>
+
+<p>To run several suites we may simply append another:</p>
+
+<pre><code>$ expresso test/a.test.js test/b.test.js
+</code></pre>
+
+<p>We can also pass a whitelist of tests to run within all suites:</p>
+
+<pre><code>$ expresso --only "foo()" --only "bar()"
+</code></pre>
+
+<p>Or several with one call:</p>
+
+<pre><code>$ expresso --only "foo(), bar()"
+</code></pre>
+
+<p>Globbing is of course possible as well:</p>
+
+<pre><code>$ expresso test/*
+</code></pre>
+
+<p>When expresso is called without any files, <em>test/*</em> is the default,
+so the following is equivalent to the command above:</p>
+
+<pre><code>$ expresso
+</code></pre>
+
+<p>If you wish to unshift a path to <code>require.paths</code> before
+running tests, you may use the <code>-I</code> or <code>--include</code> flag.</p>
+
+<pre><code>$ expresso --include lib test/*
+</code></pre>
+
+<p>The previous example is typically what I would recommend, since expresso
+supports test coverage via <a href="http://github.com/visionmedia/node-jscoverage">node-jscoverage</a> (bundled with expresso),
+so you will need to expose an instrumented version of you library.</p>
+
+<p>To instrument your library, simply run <a href="http://github.com/visionmedia/node-jscoverage">node-jscoverage</a>,
+passing the <em>src</em> and <em>dest</em> directories:</p>
+
+<pre><code>$ node-jscoverage lib lib-cov
+</code></pre>
+
+<p>Now we can run our tests again, using the <em>lib-cov</em> directory that has been
+instrumented with coverage statements:</p>
+
+<pre><code>$ expresso -I lib-cov test/*
+</code></pre>
+
+<p>The output will look similar to below, depending on your test coverage of course :)</p>
+
+<p><img src="http://dl.dropbox.com/u/6396913/cov.png" alt="node coverage" /></p>
+
+<p>To make this process easier expresso has the <em>-c</em> or <em>--cov</em> which essentially
+does the same as the two commands above. The following two commands will
+run the same tests, however one will auto-instrument, and unshift <em>lib-cov</em>,
+and the other will run tests normally:</p>
+
+<pre><code>$ expresso -I lib test/*
+$ expresso -I lib --cov test/*
+</code></pre>
+
+<p>Currently coverage is bound to the <em>lib</em> directory, however in the
+future <code>--cov</code> will most likely accept a path.</p>
+
+<h2 id="Async-Exports">Async Exports</h2>
+
+<p>Sometimes it is useful to postpone running of tests until a callback or event has fired, currently the <em>exports.foo = function(){};</em> syntax is supported for this:</p>
+
+<pre><code>setTimeout(function(){
+ exports['test async exports'] = function(){
+ assert.ok('wahoo');
+ };
+}, 100);
+</code></pre>
+
+</div>
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/docs/index.md b/node_modules/mongoose/support/expresso/docs/index.md
new file mode 100644
index 0000000..489f931
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/docs/index.md
@@ -0,0 +1,290 @@
+
+[Expresso](http://github.com/visionmedia/expresso) is a JavaScript [TDD](http://en.wikipedia.org/wiki/Test-driven_development) framework written for [nodejs](http://nodejs.org). Expresso is extremely fast, and is packed with features such as additional assertion methods, code coverage reporting, CI support, and more.
+
+## Features
+
+ - light-weight
+ - intuitive async support
+ - intuitive test runner executable
+ - test coverage support and reporting via [node-jscoverage](http://github.com/visionmedia/node-jscoverage)
+ - uses and extends the core _assert_ module
+ - `assert.eql()` alias of `assert.deepEqual()`
+ - `assert.response()` http response utility
+ - `assert.includes()`
+ - `assert.isNull()`
+ - `assert.isUndefined()`
+ - `assert.isNotNull()`
+ - `assert.isDefined()`
+ - `assert.match()`
+ - `assert.length()`
+
+## Installation
+
+To install both expresso _and_ node-jscoverage run
+the command below, which will first compile node-jscoverage:
+
+ $ make install
+
+To install expresso alone without coverage reporting run:
+
+ $ make install-expresso
+
+Install via npm:
+
+ $ npm install expresso
+
+## Examples
+
+To define tests we simply export several functions:
+
+ exports['test String#length'] = function(){
+ assert.equal(6, 'foobar'.length);
+ };
+
+Alternatively for large numbers of tests you may want to
+export your own object containing the tests, however this
+is essentially the as above:
+
+ module.exports = {
+ 'test String#length': function(){
+ assert.equal(6, 'foobar'.length);
+ }
+ };
+
+If you prefer not to use quoted keys:
+
+ exports.testsStringLength = function(){
+ assert.equal(6, 'foobar'.length);
+ };
+
+The argument passed to each callback is _beforeExit_,
+which is typically used to assert that callbacks have been
+invoked.
+
+ exports.testAsync = function(beforeExit){
+ var n = 0;
+ setTimeout(function(){
+ ++n;
+ assert.ok(true);
+ }, 200);
+ setTimeout(function(){
+ ++n;
+ assert.ok(true);
+ }, 200);
+ beforeExit(function(){
+ assert.equal(2, n, 'Ensure both timeouts are called');
+ });
+ };
+
+## Assert Utilities
+
+### assert.isNull(val[, msg])
+
+Asserts that the given _val_ is _null_.
+
+ assert.isNull(null);
+
+### assert.isNotNull(val[, msg])
+
+Asserts that the given _val_ is not _null_.
+
+ assert.isNotNull(undefined);
+ assert.isNotNull(false);
+
+### assert.isUndefined(val[, msg])
+
+Asserts that the given _val_ is _undefined_.
+
+ assert.isUndefined(undefined);
+
+### assert.isDefined(val[, msg])
+
+Asserts that the given _val_ is not _undefined_.
+
+ assert.isDefined(null);
+ assert.isDefined(false);
+
+### assert.match(str, regexp[, msg])
+
+Asserts that the given _str_ matches _regexp_.
+
+ assert.match('foobar', /^foo(bar)?/);
+ assert.match('foo', /^foo(bar)?/);
+
+### assert.length(val, n[, msg])
+
+Assert that the given _val_ has a length of _n_.
+
+ assert.length([1,2,3], 3);
+ assert.length('foo', 3);
+
+### assert.type(obj, type[, msg])
+
+Assert that the given _obj_ is typeof _type_.
+
+ assert.type(3, 'number');
+
+### assert.eql(a, b[, msg])
+
+Assert that object _b_ is equal to object _a_. This is an
+alias for the core _assert.deepEqual()_ method which does complex
+comparisons, opposed to _assert.equal()_ which uses _==_.
+
+ assert.eql('foo', 'foo');
+ assert.eql([1,2], [1,2]);
+ assert.eql({ foo: 'bar' }, { foo: 'bar' });
+
+### assert.includes(obj, val[, msg])
+
+Assert that _obj_ is within _val_. This method supports _Array_s
+and _Strings_s.
+
+ assert.includes([1,2,3], 3);
+ assert.includes('foobar', 'foo');
+ assert.includes('foobar', 'bar');
+
+### assert.response(server, req, res|fn[, msg|fn])
+
+Performs assertions on the given _server_, which should _not_ call
+listen(), as this is handled internally by expresso and the server
+is killed after all responses have completed. This method works with
+any _http.Server_ instance, so _Connect_ and _Express_ servers will work
+as well.
+
+The _req_ object may contain:
+
+ - _url_ request url
+ - _timeout_ timeout in milliseconds
+ - _method_ HTTP method
+ - _data_ request body
+ - _headers_ headers object
+
+The _res_ object may be a callback function which
+receives the response for assertions, or an object
+which is then used to perform several assertions
+on the response with the following properties:
+
+ - _body_ assert response body (regexp or string)
+ - _status_ assert response status code
+ - _header_ assert that all given headers match (unspecified are ignored, use a regexp or string)
+
+When providing _res_ you may then also pass a callback function
+as the fourth argument for additional assertions.
+
+Below are some examples:
+
+ assert.response(server, {
+ url: '/', timeout: 500
+ }, {
+ body: 'foobar'
+ });
+
+ assert.response(server, {
+ url: '/',
+ method: 'GET'
+ },{
+ body: '{"name":"tj"}',
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf8',
+ 'X-Foo': 'bar'
+ }
+ });
+
+ assert.response(server, {
+ url: '/foo',
+ method: 'POST',
+ data: 'bar baz'
+ },{
+ body: '/foo bar baz',
+ status: 200
+ }, 'Test POST');
+
+ assert.response(server, {
+ url: '/foo',
+ method: 'POST',
+ data: 'bar baz'
+ },{
+ body: '/foo bar baz',
+ status: 200
+ }, function(res){
+ // All done, do some more tests if needed
+ });
+
+ assert.response(server, {
+ url: '/'
+ }, function(res){
+ assert.ok(res.body.indexOf('tj') >= 0, 'Test assert.response() callback');
+ });
+
+
+## expresso(1)
+
+To run a single test suite (file) run:
+
+ $ expresso test/a.test.js
+
+To run several suites we may simply append another:
+
+ $ expresso test/a.test.js test/b.test.js
+
+We can also pass a whitelist of tests to run within all suites:
+
+ $ expresso --only "foo()" --only "bar()"
+
+Or several with one call:
+
+ $ expresso --only "foo(), bar()"
+
+Globbing is of course possible as well:
+
+ $ expresso test/*
+
+When expresso is called without any files, _test/*_ is the default,
+so the following is equivalent to the command above:
+
+ $ expresso
+
+If you wish to unshift a path to `require.paths` before
+running tests, you may use the `-I` or `--include` flag.
+
+ $ expresso --include lib test/*
+
+The previous example is typically what I would recommend, since expresso
+supports test coverage via [node-jscoverage](http://github.com/visionmedia/node-jscoverage) (bundled with expresso),
+so you will need to expose an instrumented version of you library.
+
+To instrument your library, simply run [node-jscoverage](http://github.com/visionmedia/node-jscoverage),
+passing the _src_ and _dest_ directories:
+
+ $ node-jscoverage lib lib-cov
+
+Now we can run our tests again, using the _lib-cov_ directory that has been
+instrumented with coverage statements:
+
+ $ expresso -I lib-cov test/*
+
+The output will look similar to below, depending on your test coverage of course :)
+
+![node coverage](http://dl.dropbox.com/u/6396913/cov.png)
+
+To make this process easier expresso has the _-c_ or _--cov_ which essentially
+does the same as the two commands above. The following two commands will
+run the same tests, however one will auto-instrument, and unshift _lib-cov_,
+and the other will run tests normally:
+
+ $ expresso -I lib test/*
+ $ expresso -I lib --cov test/*
+
+Currently coverage is bound to the _lib_ directory, however in the
+future `--cov` will most likely accept a path.
+
+## Async Exports
+
+Sometimes it is useful to postpone running of tests until a callback or event has fired, currently the _exports.foo = function(){};_ syntax is supported for this:
+
+ setTimeout(function(){
+ exports['test async exports'] = function(){
+ assert.ok('wahoo');
+ };
+ }, 100);
diff --git a/node_modules/mongoose/support/expresso/docs/layout/foot.html b/node_modules/mongoose/support/expresso/docs/layout/foot.html
new file mode 100644
index 0000000..44d85e9
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/docs/layout/foot.html
@@ -0,0 +1,3 @@
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/docs/layout/head.html b/node_modules/mongoose/support/expresso/docs/layout/head.html
new file mode 100644
index 0000000..567f62e
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/docs/layout/head.html
@@ -0,0 +1,42 @@
+<html>
+ <head>
+ <title>Expresso - TDD Framework For Node</title>
+ <style>
+ body {
+ font: 13px/1.4 "Helvetica", "Lucida Grande", Arial, sans-serif;
+ text-align: center;
+ }
+ #ribbon {
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: 10;
+ }
+ #wrapper {
+ margin: 0 auto;
+ padding: 50px 80px;
+ width: 700px;
+ text-align: left;
+ }
+ h1, h2, h3 {
+ margin: 25px 0 15px 0;
+ }
+ h1 {
+ font-size: 35px;
+ }
+ pre {
+ margin: 0 5px;
+ padding: 15px;
+ border: 1px solid #eee;
+ }
+ a {
+ color: #00aaff;
+ }
+ </style>
+ </head>
+ <body>
+ <a href="http://github.com/visionmedia/expresso">
+ <img alt="Fork me on GitHub" id="ribbon" src="http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png" />
+ </a>
+ <div id="wrapper">
+ <h1>Expresso</h1>
diff --git a/node_modules/mongoose/support/expresso/lib/bar.js b/node_modules/mongoose/support/expresso/lib/bar.js
new file mode 100644
index 0000000..e15aad4
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/lib/bar.js
@@ -0,0 +1,4 @@
+
+exports.bar = function(msg){
+ return msg || 'bar';
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/lib/foo.js b/node_modules/mongoose/support/expresso/lib/foo.js
new file mode 100644
index 0000000..15701a5
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/lib/foo.js
@@ -0,0 +1,16 @@
+
+exports.foo = function(msg){
+ if (msg) {
+ return msg;
+ } else {
+ return generateFoo();
+ }
+};
+
+function generateFoo() {
+ return 'foo';
+}
+
+function Foo(msg){
+ this.msg = msg || 'foo';
+}
diff --git a/node_modules/mongoose/support/expresso/package.json b/node_modules/mongoose/support/expresso/package.json
new file mode 100644
index 0000000..569a54b
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/package.json
@@ -0,0 +1,12 @@
+{ "name": "expresso",
+ "version": "0.7.2",
+ "description": "TDD framework, light-weight, fast, CI-friendly",
+ "author": "TJ Holowaychuk <tj@vision-media.ca>",
+ "bin": {
+ "expresso": "./bin/expresso",
+ "node-jscoverage": "./deps/jscoverage/node-jscoverage"
+ },
+ "scripts": {
+ "preinstall": "make deps/jscoverage/node-jscoverage"
+ }
+} \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/test/assert.test.js b/node_modules/mongoose/support/expresso/test/assert.test.js
new file mode 100644
index 0000000..f822595
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/test/assert.test.js
@@ -0,0 +1,91 @@
+
+/**
+ * Module dependencies.
+ */
+
+var assert = require('assert');
+
+module.exports = {
+ 'assert.eql()': function(){
+ assert.equal(assert.deepEqual, assert.eql);
+ },
+
+ 'assert.type()': function(){
+ assert.type('foobar', 'string');
+ assert.type(2, 'number');
+ assert.throws(function(){
+ assert.type([1,2,3], 'string');
+ });
+ },
+
+ 'assert.includes()': function(){
+ assert.includes('some random string', 'dom');
+ assert.throws(function(){
+ assert.include('some random string', 'foobar');
+ });
+
+ assert.includes(['foo', 'bar'], 'bar');
+ assert.includes(['foo', 'bar'], 'foo');
+ assert.includes([1,2,3], 3);
+ assert.includes([1,2,3], 2);
+ assert.includes([1,2,3], 1);
+ assert.throws(function(){
+ assert.includes(['foo', 'bar'], 'baz');
+ });
+
+ assert.throws(function(){
+ assert.includes({ wrong: 'type' }, 'foo');
+ });
+ },
+
+ 'assert.isNull()': function(){
+ assert.isNull(null);
+ assert.throws(function(){
+ assert.isNull(undefined);
+ });
+ assert.throws(function(){
+ assert.isNull(false);
+ });
+ },
+
+ 'assert.isUndefined()': function(){
+ assert.isUndefined(undefined);
+ assert.throws(function(){
+ assert.isUndefined(null);
+ });
+ assert.throws(function(){
+ assert.isUndefined(false);
+ });
+ },
+
+ 'assert.isNotNull()': function(){
+ assert.isNotNull(false);
+ assert.isNotNull(undefined);
+ assert.throws(function(){
+ assert.isNotNull(null);
+ });
+ },
+
+ 'assert.isDefined()': function(){
+ assert.isDefined(false);
+ assert.isDefined(null);
+ assert.throws(function(){
+ assert.isDefined(undefined);
+ });
+ },
+
+ 'assert.match()': function(){
+ assert.match('foobar', /foo(bar)?/);
+ assert.throws(function(){
+ assert.match('something', /rawr/);
+ });
+ },
+
+ 'assert.length()': function(){
+ assert.length('test', 4);
+ assert.length([1,2,3,4], 4);
+ assert.throws(function(){
+ assert.length([1,2,3], 4);
+ });
+ }
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/test/async.test.js b/node_modules/mongoose/support/expresso/test/async.test.js
new file mode 100644
index 0000000..d1d2036
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/test/async.test.js
@@ -0,0 +1,12 @@
+
+/**
+ * Module dependencies.
+ */
+
+var assert = require('assert');
+
+setTimeout(function(){
+ exports['test async exports'] = function(){
+ assert.ok('wahoo');
+ };
+}, 100); \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/test/bar.test.js b/node_modules/mongoose/support/expresso/test/bar.test.js
new file mode 100644
index 0000000..cfc2e11
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/test/bar.test.js
@@ -0,0 +1,13 @@
+
+/**
+ * Module dependencies.
+ */
+
+var assert = require('assert')
+ , bar = require('bar');
+
+module.exports = {
+ 'bar()': function(){
+ assert.equal('bar', bar.bar());
+ }
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/test/foo.test.js b/node_modules/mongoose/support/expresso/test/foo.test.js
new file mode 100644
index 0000000..ee5cff3
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/test/foo.test.js
@@ -0,0 +1,14 @@
+
+/**
+ * Module dependencies.
+ */
+
+var assert = require('assert')
+ , foo = require('foo');
+
+module.exports = {
+ 'foo()': function(){
+ assert.equal('foo', foo.foo());
+ assert.equal('foo', foo.foo());
+ }
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/test/http.test.js b/node_modules/mongoose/support/expresso/test/http.test.js
new file mode 100644
index 0000000..41648d2
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/test/http.test.js
@@ -0,0 +1,109 @@
+
+/**
+ * Module dependencies.
+ */
+
+var assert = require('assert')
+ , http = require('http');
+
+var server = http.createServer(function(req, res){
+ if (req.method === 'GET') {
+ if (req.url === '/delay') {
+ setTimeout(function(){
+ res.writeHead(200, {});
+ res.end('delayed');
+ }, 200);
+ } else {
+ var body = JSON.stringify({ name: 'tj' });
+ res.writeHead(200, {
+ 'Content-Type': 'application/json; charset=utf8',
+ 'Content-Length': body.length
+ });
+ res.end(body);
+ }
+ } else {
+ var body = '';
+ req.setEncoding('utf8');
+ req.addListener('data', function(chunk){ body += chunk });
+ req.addListener('end', function(){
+ res.writeHead(200, {});
+ res.end(req.url + ' ' + body);
+ });
+ }
+});
+
+var delayedServer = http.createServer(function(req, res){
+ res.writeHead(200);
+ res.end('it worked');
+});
+
+var oldListen = delayedServer.listen;
+delayedServer.listen = function(){
+ var args = arguments;
+ setTimeout(function(){
+ oldListen.apply(delayedServer, args);
+ }, 100);
+};
+
+module.exports = {
+ 'test assert.response()': function(beforeExit){
+ var called = 0;
+
+ assert.response(server, {
+ url: '/',
+ method: 'GET'
+ },{
+ body: '{"name":"tj"}',
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf8'
+ }
+ });
+
+ assert.response(server, {
+ url: '/foo',
+ method: 'POST',
+ data: 'bar baz'
+ },{
+ body: '/foo bar baz',
+ status: 200
+ }, function(res){
+ ++called;
+ assert.ok(res);
+ });
+
+ assert.response(server, {
+ url: '/foo'
+ }, function(res){
+ ++called;
+ assert.ok(res.body.indexOf('tj') >= 0, 'Test assert.response() callback');
+ });
+
+ assert.response(server,
+ { url: '/delay', timeout: 300 },
+ { body: 'delayed' });
+
+ beforeExit(function(){
+ assert.equal(2, called);
+ });
+ },
+
+ 'test assert.response() regexp': function(){
+ assert.response(server,
+ { url: '/foo', method: 'POST', data: 'foobar' },
+ { body: /^\/foo foo(bar)?/ });
+ },
+
+ 'test assert.response() regexp headers': function(){
+ assert.response(server,
+ { url: '/' },
+ { body: '{"name":"tj"}', headers: { 'Content-Type': /^application\/json/ } });
+ },
+
+ // [!] if this test doesn't pass, an uncaught ECONNREFUSED will display
+ 'test assert.response() with deferred listen()': function(){
+ assert.response(delayedServer,
+ { url: '/' },
+ { body: 'it worked' });
+ }
+};
diff --git a/node_modules/mongoose/support/expresso/test/serial/async.test.js b/node_modules/mongoose/support/expresso/test/serial/async.test.js
new file mode 100644
index 0000000..c596d5c
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/test/serial/async.test.js
@@ -0,0 +1,39 @@
+
+var assert = require('assert')
+ , setup = 0
+ , order = [];
+
+module.exports = {
+ setup: function(done){
+ ++setup;
+ done();
+ },
+
+ a: function(done){
+ assert.equal(1, setup);
+ order.push('a');
+ setTimeout(function(){
+ done();
+ }, 500);
+ },
+
+ b: function(done){
+ assert.equal(2, setup);
+ order.push('b');
+ setTimeout(function(){
+ done();
+ }, 200);
+ },
+
+ c: function(done){
+ assert.equal(3, setup);
+ order.push('c');
+ setTimeout(function(){
+ done();
+ }, 1000);
+ },
+
+ d: function(){
+ assert.eql(order, ['a', 'b', 'c']);
+ }
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/support/expresso/test/serial/http.test.js b/node_modules/mongoose/support/expresso/test/serial/http.test.js
new file mode 100644
index 0000000..7783eb5
--- /dev/null
+++ b/node_modules/mongoose/support/expresso/test/serial/http.test.js
@@ -0,0 +1,48 @@
+
+/**
+ * Module dependencies.
+ */
+
+var assert = require('assert')
+ , http = require('http');
+
+var server = http.createServer(function(req, res){
+ if (req.method === 'GET') {
+ if (req.url === '/delay') {
+ setTimeout(function(){
+ res.writeHead(200, {});
+ res.end('delayed');
+ }, 200);
+ } else {
+ var body = JSON.stringify({ name: 'tj' });
+ res.writeHead(200, {
+ 'Content-Type': 'application/json; charset=utf8',
+ 'Content-Length': body.length
+ });
+ res.end(body);
+ }
+ } else {
+ var body = '';
+ req.setEncoding('utf8');
+ req.addListener('data', function(chunk){ body += chunk });
+ req.addListener('end', function(){
+ res.writeHead(200, {});
+ res.end(req.url + ' ' + body);
+ });
+ }
+});
+
+module.exports = {
+ 'test assert.response()': function(done){
+ assert.response(server, {
+ url: '/',
+ method: 'GET'
+ },{
+ body: '{"name":"tj"}',
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf8'
+ }
+ }, done);
+ }
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/test/collection.test.js b/node_modules/mongoose/test/collection.test.js
new file mode 100644
index 0000000..54c247c
--- /dev/null
+++ b/node_modules/mongoose/test/collection.test.js
@@ -0,0 +1,116 @@
+
+var start = require('./common')
+ , mongoose = start.mongoose
+ , Collection = require('../lib/collection');
+
+module.exports = {
+
+ 'test buffering of commands until connection is established': function(beforeExit){
+ var db = mongoose.createConnection()
+ , collection = db.collection('test-buffering-collection')
+ , connected = false
+ , inserted = false;
+
+ collection.insert({ }, function(){
+ connected.should.be.true;
+ inserted = true;
+ db.close();
+ });
+
+ var uri = 'mongodb://localhost/mongoose_test';
+ db.open(process.env.MONGOOSE_TEST_URI || uri, function(err){
+ connected = !err;
+ });
+
+ beforeExit(function(){
+ connected.should.be.true;
+ inserted.should.be.true;
+ });
+ },
+
+ 'test methods that should throw (unimplemented)': function () {
+ var collection = new Collection('test', mongoose.connection)
+ , thrown = false;
+
+ try {
+ collection.getIndexes();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.update();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.save();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.insert();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.find();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.findOne();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.findAndModify();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.ensureIndex();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+ }
+
+};
diff --git a/node_modules/mongoose/test/common.js b/node_modules/mongoose/test/common.js
new file mode 100644
index 0000000..ffb3783
--- /dev/null
+++ b/node_modules/mongoose/test/common.js
@@ -0,0 +1,145 @@
+
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('../')
+ , should = require('should')
+ , Table = require('cli-table')
+ , Mongoose = mongoose.Mongoose
+ , Collection = mongoose.Collection
+ , Assertion = should.Assertion
+ , startTime = Date.now()
+ , queryCount = 0
+ , opened = 0
+ , closed = 0;
+
+/**
+ * Override all Collection related queries to keep count
+ */
+
+[ 'ensureIndex'
+ , 'findAndModify'
+ , 'findOne'
+ , 'find'
+ , 'insert'
+ , 'save'
+ , 'update'
+ , 'remove'
+ , 'count'
+ , 'distinct'
+].forEach(function (method) {
+
+ var oldMethod = Collection.prototype[method];
+
+ Collection.prototype[method] = function () {
+ queryCount++;
+ return oldMethod.apply(this, arguments);
+ };
+
+});
+
+/**
+ * Override Collection#onOpen to keep track of connections
+ */
+
+var oldOnOpen = Collection.prototype.onOpen;
+
+Collection.prototype.onOpen = function(){
+ opened++;
+ return oldOnOpen.apply(this, arguments);
+};
+
+/**
+ * Override Collection#onClose to keep track of disconnections
+ */
+
+var oldOnClose = Collection.prototype.onClose;
+
+Collection.prototype.onClose = function(){
+ closed++;
+ return oldOnClose.apply(this, arguments);
+};
+
+/**
+ * Assert that a connection is open or that mongoose connections are open.
+ * Examples:
+ * mongoose.should.be.connected;
+ * db.should.be.connected;
+ *
+ * @api public
+ */
+
+Assertion.prototype.__defineGetter__('connected', function(){
+ if (this.obj instanceof Mongoose)
+ this.obj.connections.forEach(function(connected){
+ c.should.be.connected;
+ });
+ else
+ this.obj.readyState.should.eql(1);
+});
+
+/**
+ * Assert that a connection is closed or that a mongoose connections are closed.
+ * Examples:
+ * mongoose.should.be.disconnected;
+ * db.should.be.disconnected;
+ *
+ * @api public
+ */
+
+Assertion.prototype.__defineGetter__('disconnected', function(){
+ if (this.obj instanceof Mongoose)
+ this.obj.connections.forEach(function(){
+ c.should.be.disconnected;
+ });
+ else
+ this.obj.readyState.should.eql(0);
+});
+
+/**
+ * Create a connection to the test database.
+ * You can set the environmental variable MONGOOSE_TEST_URI to override this.
+ *
+ * @api private
+ */
+
+module.exports = function (options) {
+ var uri;
+
+ if (options && options.uri) {
+ uri = options.uri;
+ delete options.uri;
+ } else {
+ uri = process.env.MONGOOSE_TEST_URI ||
+ 'mongodb://localhost/mongoose_test'
+ }
+
+ return mongoose.createConnection(uri, options);
+};
+
+/**
+ * Provide stats for tests
+ */
+
+process.on('beforeExit', function(){
+ var table = new Table({
+ head: ['Stat', 'Time (ms)']
+ , colWidths: [23, 15]
+ });
+
+ table.push(
+ ['Queries run', queryCount]
+ , ['Time ellapsed', Date.now() - startTime]
+ , ['Connections opened', opened]
+ , ['Connections closed', closed]
+ );
+
+ console.error(table.toString());
+});
+
+/**
+ * Module exports.
+ */
+
+module.exports.mongoose = mongoose;
diff --git a/node_modules/mongoose/test/connection.test.js b/node_modules/mongoose/test/connection.test.js
new file mode 100644
index 0000000..92c162a
--- /dev/null
+++ b/node_modules/mongoose/test/connection.test.js
@@ -0,0 +1,310 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , Schema = mongoose.Schema
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test closing a connection that\'s already closed': function (beforeExit) {
+ var db = mongoose.createConnection()
+ , called = false;
+
+ db.readyState.should.eql(0);
+ db.close(function (err) {
+ should.strictEqual(err, null);
+ called = true;
+ });
+
+ beforeExit(function () {
+ called.should.be.true;
+ });
+ },
+
+ 'test connection args': function (beforeExit) {
+ var db = mongoose.createConnection('mongodb://localhost/fake');
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual(undefined, db.pass);
+ should.strictEqual(undefined, db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal(27017);
+ db.close();
+
+ db = mongoose.createConnection('mongodb://localhost:27000/fake');
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual(undefined, db.pass);
+ should.strictEqual(undefined, db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal('27000');
+ db.close();
+
+ db = mongoose.createConnection('mongodb://aaron:psw@localhost:27000/fake');
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual('psw', db.pass);
+ should.strictEqual('aaron', db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal('27000');
+ db.close();
+
+ db = mongoose.createConnection('mongodb://aaron:psw@localhost:27000/fake', { db: { forceServerObjectId: true }});
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.close();
+
+ db = mongoose.createConnection('mongodb://aaron:psw@localhost:27000/fake', { server: { auto_reconnect: false }});
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.false;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.close();
+
+ var called1 = false;
+ db = mongoose.createConnection('mongodb://aaron:psw@localhost:27000/fake', { server: { auto_reconnect: true }}, function () {
+ called1 = true;
+ });
+ beforeExit(function () {
+ called1.should.be.true;
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.close();
+
+ var called2 = false;
+ db = mongoose.createConnection('mongodb://localhost/fake', function () {
+ called2 = true;
+ });
+ beforeExit(function () {
+ called2.should.be.true;
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal(27017);
+ db.close();
+
+ db = mongoose.createConnection('mongodb:///fake', function (err) {
+ err.message.should.equal('Missing connection hostname.');
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual(undefined, db.name);
+ should.strictEqual(undefined, db.host);
+ should.strictEqual(undefined, db.port);
+ db.close();
+
+ db = mongoose.createConnection('mongodb://localhost', function (err) {
+ err.message.should.equal('Missing connection database.');
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual(undefined, db.name);
+ should.strictEqual(undefined, db.host);
+ should.strictEqual(undefined, db.port);
+ db.close();
+
+ var called3 = false;
+ db = mongoose.createConnection('127.0.0.1', 'faker', 28000, { server: { auto_reconnect: false }}, function () {
+ called3 = true;
+ });
+ beforeExit(function () {
+ called3.should.be.true;
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.false;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(28000);
+ db.close();
+
+ var called4 = false;
+ db = mongoose.createConnection('127.0.0.1', 'faker', 28000, function () {
+ called4 = true;
+ });
+ beforeExit(function () {
+ called4.should.be.true;
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(28000);
+ db.close();
+
+ db = mongoose.createConnection('127.0.0.1', 'faker', 28000, { server: { auto_reconnect: true }});
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(28000);
+ db.close();
+
+ db = mongoose.createConnection('127.0.0.1', 'faker', 28001);
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(28001);
+ db.close();
+
+ db = mongoose.createConnection('127.0.0.1', 'faker', { blah: 1 });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.options.blah.should.equal(1);
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(27017);
+ db.close();
+
+ var called5 = false
+ db = mongoose.createConnection('127.0.0.1', 'faker', function () {
+ called5 = true;
+ });
+ beforeExit(function () {
+ called5.should.be.true;
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(27017);
+ db.close();
+
+ db = mongoose.createConnection('127.0.0.1', 'faker');
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(27017);
+ db.close();
+
+ // Test connecting using user/pass in hostname
+ db = mongoose.createConnection('aaron:psw@localhost', 'fake', 27000);
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual('psw', db.pass);
+ should.strictEqual('aaron', db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal(27000);
+ db.close();
+
+ // Test connecting using user/pass options
+ db = mongoose.createConnection('localhost', 'fake', 27000, {user: 'aaron', pass: 'psw'});
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual('psw', db.pass);
+ should.strictEqual('aaron', db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal(27000);
+ db.close();
+
+ // Test connecting using only user option - which shouldn't work
+ db = mongoose.createConnection('localhost', 'fake', 27000, {user: 'no_pass'});
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual(undefined, db.pass);
+ should.strictEqual(undefined, db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal(27000);
+ db.close();
+ },
+
+ 'connection.model allows passing a schema': function () {
+ var db = start();
+ var MyModel = db.model('MyModelasdf', new Schema({
+ name: String
+ }));
+
+ MyModel.schema.should.be.an.instanceof(Schema);
+ MyModel.prototype.schema.should.be.an.instanceof(Schema);
+
+ var m = new MyModel({name:'aaron'});
+ m.name.should.eql('aaron');
+ db.close();
+ },
+
+ 'connection error event fires with one listener': function (exit) {
+ var db= start({ uri: 'mongodb://localasdfads/fakeeee'})
+ , called = false;
+ db.on('error', function () {
+ // this callback has no params which triggered the bug #759
+ called = true;
+ });
+ exit(function () {
+ called.should.be.true;
+ });
+ }
+
+};
diff --git a/node_modules/mongoose/test/crash.test.js b/node_modules/mongoose/test/crash.test.js
new file mode 100644
index 0000000..67e6c86
--- /dev/null
+++ b/node_modules/mongoose/test/crash.test.js
@@ -0,0 +1,36 @@
+
+// GH-407
+
+var start = require('./common')
+ , mongoose = start.mongoose
+ , should = require('should')
+
+exports['test mongodb crash with invalid objectid string'] = function () {
+ var db = mongoose.createConnection("mongodb://localhost/test-crash");
+
+ var IndexedGuy = new mongoose.Schema({
+ name: { type: String }
+ });
+
+ var Guy = db.model('Guy', IndexedGuy);
+ Guy.find({
+ _id: {
+ $in: [
+ '4e0de2a6ee47bff98000e145',
+ '4e137bd81a6a8e00000007ac',
+ '',
+ '4e0e2ca0795666368603d974']
+ }
+ }, function (err) {
+ db.close();
+
+ // should is acting strange
+ try {
+ should.strictEqual(err.message, "Invalid ObjectId");
+ } catch (er) {
+ console.error(err);
+ throw er;
+ }
+ });
+
+}
diff --git a/node_modules/mongoose/test/document.strict.test.js b/node_modules/mongoose/test/document.strict.test.js
new file mode 100644
index 0000000..f9b8118
--- /dev/null
+++ b/node_modules/mongoose/test/document.strict.test.js
@@ -0,0 +1,178 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Query = require('../lib/query')
+ , Schema = mongoose.Schema
+ , SchemaType = mongoose.SchemaType
+ , CastError = SchemaType.CastError
+ , ValidatorError = SchemaType.ValidatorError
+ , ValidationError = mongoose.Document.ValidationError
+ , ObjectId = Schema.ObjectId
+ , DocumentObjectId = mongoose.Types.ObjectId
+ , DocumentArray = mongoose.Types.DocumentArray
+ , EmbeddedDocument = mongoose.Types.Embedded
+ , MongooseNumber = mongoose.Types.Number
+ , MongooseArray = mongoose.Types.Array
+ , MongooseError = mongoose.Error;
+
+module.exports = {
+
+ 'strict mode': function(){
+ var db = start();
+
+ var lax = new Schema({
+ ts : { type: Date, default: Date.now }
+ , content: String
+ });
+
+ var strict = new Schema({
+ ts : { type: Date, default: Date.now }
+ , content: String
+ }, { strict: true });
+
+ var Lax = db.model('Lax', lax);
+ var Strict = db.model('Strict', strict);
+
+ var l = new Lax({content: 'sample', rouge: 'data'});
+ l._strictMode.should.be.false;
+ l = l.toObject();
+ l.content.should.equal('sample')
+ l.rouge.should.equal('data');
+ should.exist(l.rouge);
+
+ var s = new Strict({content: 'sample', rouge: 'data'});
+ s._strictMode.should.be.true;
+ s = s.toObject();
+ s.should.have.property('ts');
+ s.content.should.equal('sample');
+ s.should.not.have.property('rouge');
+ should.not.exist(s.rouge);
+
+ // instance override
+ var instance = new Lax({content: 'sample', rouge: 'data'}, true);
+ instance._strictMode.should.be.true;
+ instance = instance.toObject();
+ instance.content.should.equal('sample')
+ should.not.exist(instance.rouge);
+ instance.should.have.property('ts')
+
+ // hydrate works as normal, but supports the schema level flag.
+ var s2 = new Strict({content: 'sample', rouge: 'data'}, false);
+ s2._strictMode.should.be.false;
+ s2 = s2.toObject();
+ s2.should.have.property('ts')
+ s2.content.should.equal('sample');
+ s2.should.have.property('rouge');
+ should.exist(s2.rouge);
+
+ // testing init
+ var s3 = new Strict();
+ s3.init({content: 'sample', rouge: 'data'});
+ var s3obj = s3.toObject();
+ s3.content.should.equal('sample');
+ s3.should.not.have.property('rouge');
+ should.not.exist(s3.rouge);
+
+ // strict on create
+ Strict.create({content: 'sample2', rouge: 'data'}, function(err, doc){
+ db.close();
+ doc.content.should.equal('sample2');
+ doc.should.not.have.property('rouge');
+ should.not.exist(doc.rouge);
+ });
+ },
+
+ 'embedded doc strict mode': function(){
+ var db = start();
+
+ var lax = new Schema({
+ ts : { type: Date, default: Date.now }
+ , content: String
+ });
+
+ var strict = new Schema({
+ ts : { type: Date, default: Date.now }
+ , content: String
+ }, { strict: true });
+
+ var Lax = db.model('EmbeddedLax', new Schema({ dox: [lax] }), 'embdoc'+random());
+ var Strict = db.model('EmbeddedStrict', new Schema({ dox: [strict] }), 'embdoc'+random());
+
+ var l = new Lax({ dox: [{content: 'sample', rouge: 'data'}] });
+ l.dox[0]._strictMode.should.be.false;
+ l = l.dox[0].toObject();
+ l.content.should.equal('sample')
+ l.rouge.should.equal('data');
+ should.exist(l.rouge);
+
+ var s = new Strict({ dox: [{content: 'sample', rouge: 'data'}] });
+ s.dox[0]._strictMode.should.be.true;
+ s = s.dox[0].toObject();
+ s.should.have.property('ts');
+ s.content.should.equal('sample');
+ s.should.not.have.property('rouge');
+ should.not.exist(s.rouge);
+
+ // testing init
+ var s3 = new Strict();
+ s3.init({dox: [{content: 'sample', rouge: 'data'}]});
+ var s3obj = s3.toObject();
+ s3.dox[0].content.should.equal('sample');
+ s3.dox[0].should.not.have.property('rouge');
+ should.not.exist(s3.dox[0].rouge);
+
+ // strict on create
+ Strict.create({dox:[{content: 'sample2', rouge: 'data'}]}, function(err, doc){
+ db.close();
+ doc.dox[0].content.should.equal('sample2');
+ doc.dox[0].should.not.have.property('rouge');
+ should.not.exist(doc.dox[0].rouge);
+ });
+ },
+
+ 'strict mode virtuals': function () {
+ var db = start();
+
+ var getCount = 0
+ , setCount = 0;
+
+ var strictSchema = new Schema({
+ email: String
+ , prop: String
+ }, {strict: true});
+
+ strictSchema
+ .virtual('myvirtual')
+ .get(function() {
+ getCount++;
+ return 'ok';
+ })
+ .set(function(v) {
+ setCount++;
+ this.prop = v;
+ });
+
+ var StrictModel = db.model('StrictVirtual', strictSchema);
+
+ var strictInstance = new StrictModel({
+ email: 'hunter@skookum.com'
+ , myvirtual: 'test'
+ });
+
+ db.close();
+ getCount.should.equal(0);
+ setCount.should.equal(1);
+
+ strictInstance.myvirtual = 'anotherone';
+ var myvirtual = strictInstance.myvirtual;
+
+ getCount.should.equal(1);
+ setCount.should.equal(2);
+ }
+}
diff --git a/node_modules/mongoose/test/document.test.js b/node_modules/mongoose/test/document.test.js
new file mode 100644
index 0000000..7c1b244
--- /dev/null
+++ b/node_modules/mongoose/test/document.test.js
@@ -0,0 +1,912 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , Schema = mongoose.Schema
+ , ObjectId = Schema.ObjectId
+ , Document = require('../lib/document')
+ , DocumentObjectId = mongoose.Types.ObjectId;
+
+/**
+ * Test Document constructor.
+ */
+
+function TestDocument () {
+ Document.apply(this, arguments);
+};
+
+/**
+ * Inherits from Document.
+ */
+
+TestDocument.prototype.__proto__ = Document.prototype;
+
+/**
+ * Set a dummy schema to simulate compilation.
+ */
+
+var em = new Schema({ title: String, body: String });
+var schema = TestDocument.prototype.schema = new Schema({
+ test : String
+ , oids : [ObjectId]
+ , numbers : [Number]
+ , nested : {
+ age : Number
+ , cool : ObjectId
+ , deep : { x: String }
+ , path : String
+ , setr : String
+ }
+ , nested2 : {
+ nested: String
+ , yup : {
+ nested : Boolean
+ , yup : String
+ , age : Number
+ }
+ }
+ , em: [em]
+});
+
+schema.virtual('nested.agePlus2').get(function (v) {
+ return this.nested.age + 2;
+});
+schema.virtual('nested.setAge').set(function (v) {
+ this.nested.age = v;
+});
+schema.path('nested.path').get(function (v) {
+ return this.nested.age + (v ? v : '');
+});
+schema.path('nested.setr').set(function (v) {
+ return v + ' setter';
+});
+
+/**
+ * Method subject to hooks. Simply fires the callback once the hooks are
+ * executed.
+ */
+
+TestDocument.prototype.hooksTest = function(fn){
+ fn(null, arguments);
+};
+
+/**
+ * Test.
+ */
+
+module.exports = {
+ 'test shortcut getters': function(){
+ var doc = new TestDocument();
+ doc.init({
+ test : 'test'
+ , oids : []
+ , nested : {
+ age : 5
+ , cool : DocumentObjectId.fromString('4c6c2d6240ced95d0e00003c')
+ , path : 'my path'
+ }
+ });
+
+ doc.test.should.eql('test');
+ doc.oids.should.be.an.instanceof(Array);
+ (doc.nested.age == 5).should.be.true;
+ DocumentObjectId.toString(doc.nested.cool).should.eql('4c6c2d6240ced95d0e00003c');
+ doc.nested.agePlus2.should.eql(7);
+ doc.nested.path.should.eql('5my path');
+ doc.nested.setAge = 10;
+ (doc.nested.age == 10).should.be.true;
+ doc.nested.setr = 'set it';
+ doc.getValue('nested.setr').should.eql('set it setter');
+
+ var doc2 = new TestDocument();
+ doc2.init({
+ test : 'toop'
+ , oids : []
+ , nested : {
+ age : 2
+ , cool : DocumentObjectId.fromString('4cf70857337498f95900001c')
+ , deep : { x: 'yay' }
+ }
+ });
+
+ doc2.test.should.eql('toop');
+ doc2.oids.should.be.an.instanceof(Array);
+ (doc2.nested.age == 2).should.be.true;
+
+ // GH-366
+ should.strictEqual(doc2.nested.bonk, undefined);
+ should.strictEqual(doc2.nested.nested, undefined);
+ should.strictEqual(doc2.nested.test, undefined);
+ should.strictEqual(doc2.nested.age.test, undefined);
+ should.strictEqual(doc2.nested.age.nested, undefined);
+ should.strictEqual(doc2.oids.nested, undefined);
+ should.strictEqual(doc2.nested.deep.x, 'yay');
+ should.strictEqual(doc2.nested.deep.nested, undefined);
+ should.strictEqual(doc2.nested.deep.cool, undefined);
+ should.strictEqual(doc2.nested2.yup.nested, undefined);
+ should.strictEqual(doc2.nested2.yup.nested2, undefined);
+ should.strictEqual(doc2.nested2.yup.yup, undefined);
+ should.strictEqual(doc2.nested2.yup.age, undefined);
+ doc2.nested2.yup.should.be.a('object');
+
+ doc2.nested2.yup = {
+ age: 150
+ , yup: "Yesiree"
+ , nested: true
+ };
+
+ should.strictEqual(doc2.nested2.nested, undefined);
+ should.strictEqual(doc2.nested2.yup.nested, true);
+ should.strictEqual(doc2.nested2.yup.yup, "Yesiree");
+ (doc2.nested2.yup.age == 150).should.be.true;
+ doc2.nested2.nested = "y";
+ should.strictEqual(doc2.nested2.nested, "y");
+ should.strictEqual(doc2.nested2.yup.nested, true);
+ should.strictEqual(doc2.nested2.yup.yup, "Yesiree");
+ (doc2.nested2.yup.age == 150).should.be.true;
+
+ DocumentObjectId.toString(doc2.nested.cool).should.eql('4cf70857337498f95900001c');
+
+ doc.oids.should.not.equal(doc2.oids);
+ },
+
+ 'test shortcut setters': function () {
+ var doc = new TestDocument();
+
+ doc.init({
+ test : 'Test'
+ , nested : {
+ age : 5
+ }
+ });
+
+ doc.isModified('test').should.be.false;
+ doc.test = 'Woot';
+ doc.test.should.eql('Woot');
+ doc.isModified('test').should.be.true;
+
+ doc.isModified('nested.age').should.be.false;
+ doc.nested.age = 2;
+ (doc.nested.age == 2).should.be.true;
+ doc.isModified('nested.age').should.be.true;
+ },
+
+ 'test accessor of id': function () {
+ var doc = new TestDocument();
+ doc._id.should.be.an.instanceof(DocumentObjectId);
+ },
+
+ 'test shortcut of id hexString': function () {
+ var doc = new TestDocument()
+ , _id = doc._id.toString();
+ doc.id.should.be.a('string');
+ },
+
+ 'test toObject clone': function(){
+ var doc = new TestDocument();
+ doc.init({
+ test : 'test'
+ , oids : []
+ , nested : {
+ age : 5
+ , cool : new DocumentObjectId
+ }
+ });
+
+ var copy = doc.toObject();
+
+ copy.test._marked = true;
+ copy.nested._marked = true;
+ copy.nested.age._marked = true;
+ copy.nested.cool._marked = true;
+
+ should.strictEqual(doc._doc.test._marked, undefined);
+ should.strictEqual(doc._doc.nested._marked, undefined);
+ should.strictEqual(doc._doc.nested.age._marked, undefined);
+ should.strictEqual(doc._doc.nested.cool._marked, undefined);
+ },
+
+ 'toObject options': function () {
+ var doc = new TestDocument();
+
+ doc.init({
+ test : 'test'
+ , oids : []
+ , nested : {
+ age : 5
+ , cool : DocumentObjectId.fromString('4c6c2d6240ced95d0e00003c')
+ , path : 'my path'
+ }
+ });
+
+ var clone = doc.toObject({ getters: true, virtuals: false });
+
+ clone.test.should.eql('test');
+ clone.oids.should.be.an.instanceof(Array);
+ (clone.nested.age == 5).should.be.true;
+ DocumentObjectId.toString(clone.nested.cool).should.eql('4c6c2d6240ced95d0e00003c');
+ clone.nested.path.should.eql('5my path');
+ should.equal(undefined, clone.nested.agePlus2);
+
+ clone = doc.toObject({ virtuals: true });
+
+ clone.test.should.eql('test');
+ clone.oids.should.be.an.instanceof(Array);
+ (clone.nested.age == 5).should.be.true;
+ DocumentObjectId.toString(clone.nested.cool).should.eql('4c6c2d6240ced95d0e00003c');
+ clone.nested.path.should.eql('my path');
+ clone.nested.agePlus2.should.eql(7);
+
+ clone = doc.toObject({ getters: true });
+
+ clone.test.should.eql('test');
+ clone.oids.should.be.an.instanceof(Array);
+ (clone.nested.age == 5).should.be.true;
+ DocumentObjectId.toString(clone.nested.cool).should.eql('4c6c2d6240ced95d0e00003c');
+ clone.nested.path.should.eql('5my path');
+ clone.nested.agePlus2.should.eql(7);
+ },
+
+ 'test hooks system': function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , awaiting = 0
+ , called = false;
+
+ // serial
+ doc.pre('hooksTest', function(next){
+ steps++;
+ setTimeout(function(){
+ // make sure next step hasn't executed yet
+ steps.should.eql(1);
+ next();
+ }, 50);
+ });
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ next();
+ });
+
+ // parallel
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ steps.should.eql(3);
+ setTimeout(function(){
+ steps.should.eql(4);
+ }, 10);
+ setTimeout(function(){
+ steps++;
+ done();
+ }, 110);
+ next();
+ });
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ setTimeout(function(){
+ steps.should.eql(4);
+ }, 10);
+ setTimeout(function(){
+ steps++;
+ done();
+ }, 110);
+ next();
+ });
+
+ doc.hooksTest(function(err){
+ should.strictEqual(err, null);
+ steps++;
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(7);
+ called.should.be.true;
+ });
+ },
+
+ 'test that calling next twice doesnt break': function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ next();
+ next();
+ });
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ next();
+ });
+
+ doc.hooksTest(function(err){
+ should.strictEqual(err, null);
+ steps++;
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(3);
+ called.should.be.true;
+ });
+ },
+
+ 'test that calling done twice doesnt break': function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done();
+ done();
+ });
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done();
+ done();
+ });
+
+ doc.hooksTest(function(err){
+ should.strictEqual(err, null);
+ steps++;
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(3);
+ called.should.be.true;
+ });
+ },
+
+ 'test that calling done twice on the same doesnt mean completion':
+ function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done();
+ done();
+ });
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ });
+
+ doc.hooksTest(function(err){
+ should.strictEqual(err, null);
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(2);
+ called.should.be.false;
+ });
+ },
+
+ 'test hooks system errors from a serial hook': function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ next();
+ });
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ next(new Error);
+ });
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ });
+
+ doc.hooksTest(function(err){
+ err.should.be.an.instanceof(Error);
+ steps++;
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(3);
+ called.should.be.true;
+ });
+ },
+
+ 'test hooks system erros from last serial hook': function(beforeExit){
+ var doc = new TestDocument()
+ , called = false;
+
+ doc.pre('hooksTest', function(next){
+ next(new Error());
+ });
+
+ doc.hooksTest(function(err){
+ err.should.be.an.instanceof(Error);
+ called = true;
+ });
+
+ beforeExit(function(){
+ called.should.be.true;
+ });
+ },
+
+ 'test mutating incoming args via middleware': function (beforeExit) {
+ var doc = new TestDocument();
+
+ doc.pre('set', function(next, path, val){
+ next(path, 'altered-' + val);
+ });
+
+ doc.set('test', 'me');
+
+ beforeExit(function(){
+ doc.test.should.equal('altered-me');
+ });
+ },
+
+ 'test hooks system errors from a parallel hook': function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done();
+ });
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done();
+ });
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done(new Error);
+ });
+
+ doc.hooksTest(function(err){
+ err.should.be.an.instanceof(Error);
+ steps++;
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(4);
+ called.should.be.true;
+ });
+ },
+
+ 'test that its not necessary to call the last next in the parallel chain':
+ function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', function(next, done){
+ next();
+ done();
+ });
+
+ doc.pre('hooksTest', function(next, done){
+ done();
+ });
+
+ doc.hooksTest(function(){
+ called = true;
+ });
+
+ beforeExit(function(){
+ called.should.be.true;
+ });
+ },
+
+ 'test passing two arguments to a method subject to hooks and return value':
+ function (beforeExit) {
+ var doc = new TestDocument()
+ , called = false;
+
+ doc.pre('hooksTest', function (next) {
+ next();
+ });
+
+ doc.hooksTest(function (err, args) {
+ args.should.have.length(2);
+ args[1].should.eql('test');
+ called = true;
+ }, 'test')
+
+ beforeExit(function () {
+ called.should.be.true;
+ });
+ },
+
+ // gh-746
+ 'hooking set works with document arrays': function () {
+ var db = start();
+
+ var child = new Schema({ text: String });
+
+ child.pre('set', function (next, path, value, type) {
+ next(path, value, type);
+ });
+
+ var schema = new Schema({
+ name: String
+ , e: [child]
+ });
+
+ var S = db.model('docArrayWithHookedSet', schema);
+
+ var s = new S({ name: "test" });
+ s.e = [{ text: 'hi' }];
+ s.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ },
+
+ 'test jsonifying an object': function () {
+ var doc = new TestDocument({ test: 'woot' })
+ , oidString = DocumentObjectId.toString(doc._id);
+
+ // convert to json string
+ var json = JSON.stringify(doc);
+
+ // parse again
+ var obj = JSON.parse(json);
+
+ obj.test.should.eql('woot');
+ obj._id.should.eql(oidString);
+ },
+
+ 'toObject should not set undefined values to null': function () {
+ var doc = new TestDocument()
+ , obj = doc.toObject();
+
+ delete obj._id;
+ obj.should.eql({ numbers: [], oids: [], em: [] });
+ },
+
+ // GH-209
+ 'MongooseErrors should be instances of Error': function () {
+ var MongooseError = require('../lib/error')
+ , err = new MongooseError("Some message");
+ err.should.be.an.instanceof(Error);
+ },
+ 'ValidationErrors should be instances of Error': function () {
+ var ValidationError = Document.ValidationError
+ , err = new ValidationError(new TestDocument);
+ err.should.be.an.instanceof(Error);
+ },
+
+ 'methods on embedded docs should work': function () {
+ var db = start()
+ , ESchema = new Schema({ name: String })
+
+ ESchema.methods.test = function () {
+ return this.name + ' butter';
+ }
+ ESchema.statics.ten = function () {
+ return 10;
+ }
+
+ var E = db.model('EmbeddedMethodsAndStaticsE', ESchema);
+ var PSchema = new Schema({ embed: [ESchema] });
+ var P = db.model('EmbeddedMethodsAndStaticsP', PSchema);
+
+ var p = new P({ embed: [{name: 'peanut'}] });
+ should.equal('function', typeof p.embed[0].test);
+ should.equal('function', typeof E.ten);
+ p.embed[0].test().should.equal('peanut butter');
+ E.ten().should.equal(10);
+
+ // test push casting
+ p = new P;
+ p.embed.push({name: 'apple'});
+ should.equal('function', typeof p.embed[0].test);
+ should.equal('function', typeof E.ten);
+ p.embed[0].test().should.equal('apple butter');
+
+ db.close();
+ },
+
+ 'setting a positional path does not cast value to array': function () {
+ var doc = new TestDocument;
+ doc.init({ numbers: [1,3] });
+ doc.numbers[0].should.eql(1);
+ doc.numbers[1].valueOf().should.eql(3);
+ doc.set('numbers.1', 2);
+ doc.numbers[0].should.eql(1);
+ doc.numbers[1].valueOf().should.eql(2);
+ },
+
+ 'no maxListeners warning should occur': function () {
+ var db = start();
+
+ var traced = false;
+ var trace = console.trace;
+
+ console.trace = function () {
+ traced = true;
+ console.trace = trace;
+ }
+
+ var schema = new Schema({
+ title: String
+ , embed1: [new Schema({name:String})]
+ , embed2: [new Schema({name:String})]
+ , embed3: [new Schema({name:String})]
+ , embed4: [new Schema({name:String})]
+ , embed5: [new Schema({name:String})]
+ , embed6: [new Schema({name:String})]
+ , embed7: [new Schema({name:String})]
+ , embed8: [new Schema({name:String})]
+ , embed9: [new Schema({name:String})]
+ , embed10: [new Schema({name:String})]
+ , embed11: [new Schema({name:String})]
+ });
+
+ var S = db.model('noMaxListeners', schema);
+
+ var s = new S({ title: "test" });
+ db.close();
+ traced.should.be.false
+ },
+
+ 'isSelected': function () {
+ var doc = new TestDocument();
+
+ doc.init({
+ test : 'test'
+ , numbers : [4,5,6,7]
+ , nested : {
+ age : 5
+ , cool : DocumentObjectId.fromString('4c6c2d6240ced95d0e00003c')
+ , path : 'my path'
+ , deep : { x: 'a string' }
+ }
+ , notapath: 'i am not in the schema'
+ , em: [{ title: 'gocars' }]
+ });
+
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.true;
+ doc.isSelected('numbers').should.be.true;
+ doc.isSelected('oids').should.be.true; // even if no data
+ doc.isSelected('nested').should.be.true;
+ doc.isSelected('nested.age').should.be.true;
+ doc.isSelected('nested.cool').should.be.true;
+ doc.isSelected('nested.path').should.be.true;
+ doc.isSelected('nested.deep').should.be.true;
+ doc.isSelected('nested.nope').should.be.true; // not a path
+ doc.isSelected('nested.deep.x').should.be.true;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+ doc.isSelected('nested.deep.y').should.be.true; // not a path
+ doc.isSelected('noway').should.be.true; // not a path
+ doc.isSelected('notapath').should.be.true; // not a path but in the _doc
+ doc.isSelected('em').should.be.true;
+ doc.isSelected('em.title').should.be.true;
+ doc.isSelected('em.body').should.be.true;
+ doc.isSelected('em.nonpath').should.be.true; // not a path
+
+ var selection = {
+ 'test': 1
+ , 'numbers': 1
+ , 'nested.deep': 1
+ , 'oids': 1
+ }
+
+ doc = new TestDocument(undefined, selection);
+
+ doc.init({
+ test : 'test'
+ , numbers : [4,5,6,7]
+ , nested : {
+ deep : { x: 'a string' }
+ }
+ });
+
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.true;
+ doc.isSelected('numbers').should.be.true;
+ doc.isSelected('oids').should.be.true; // even if no data
+ doc.isSelected('nested').should.be.true;
+ doc.isSelected('nested.age').should.be.false;
+ doc.isSelected('nested.cool').should.be.false;
+ doc.isSelected('nested.path').should.be.false;
+ doc.isSelected('nested.deep').should.be.true;
+ doc.isSelected('nested.nope').should.be.false;
+ doc.isSelected('nested.deep.x').should.be.true;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+ doc.isSelected('nested.deep.y').should.be.true;
+ doc.isSelected('noway').should.be.false;
+ doc.isSelected('notapath').should.be.false;
+ doc.isSelected('em').should.be.false;
+ doc.isSelected('em.title').should.be.false;
+ doc.isSelected('em.body').should.be.false;
+ doc.isSelected('em.nonpath').should.be.false;
+
+ var selection = {
+ 'em.title': 1
+ }
+
+ doc = new TestDocument(undefined, selection);
+
+ doc.init({
+ em: [{ title: 'one' }]
+ });
+
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.false;
+ doc.isSelected('numbers').should.be.false;
+ doc.isSelected('oids').should.be.false;
+ doc.isSelected('nested').should.be.false;
+ doc.isSelected('nested.age').should.be.false;
+ doc.isSelected('nested.cool').should.be.false;
+ doc.isSelected('nested.path').should.be.false;
+ doc.isSelected('nested.deep').should.be.false;
+ doc.isSelected('nested.nope').should.be.false;
+ doc.isSelected('nested.deep.x').should.be.false;
+ doc.isSelected('nested.deep.x.no').should.be.false;
+ doc.isSelected('nested.deep.y').should.be.false;
+ doc.isSelected('noway').should.be.false;
+ doc.isSelected('notapath').should.be.false;
+ doc.isSelected('em').should.be.true;
+ doc.isSelected('em.title').should.be.true;
+ doc.isSelected('em.body').should.be.false;
+ doc.isSelected('em.nonpath').should.be.false;
+
+ var selection = {
+ 'em': 0
+ }
+
+ doc = new TestDocument(undefined, selection);
+ doc.init({
+ test : 'test'
+ , numbers : [4,5,6,7]
+ , nested : {
+ age : 5
+ , cool : DocumentObjectId.fromString('4c6c2d6240ced95d0e00003c')
+ , path : 'my path'
+ , deep : { x: 'a string' }
+ }
+ , notapath: 'i am not in the schema'
+ });
+
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.true;
+ doc.isSelected('numbers').should.be.true;
+ doc.isSelected('oids').should.be.true;
+ doc.isSelected('nested').should.be.true;
+ doc.isSelected('nested.age').should.be.true;
+ doc.isSelected('nested.cool').should.be.true;
+ doc.isSelected('nested.path').should.be.true;
+ doc.isSelected('nested.deep').should.be.true;
+ doc.isSelected('nested.nope').should.be.true;
+ doc.isSelected('nested.deep.x').should.be.true;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+ doc.isSelected('nested.deep.y').should.be.true;
+ doc.isSelected('noway').should.be.true;
+ doc.isSelected('notapath').should.be.true;
+ doc.isSelected('em').should.be.false;
+ doc.isSelected('em.title').should.be.false;
+ doc.isSelected('em.body').should.be.false;
+ doc.isSelected('em.nonpath').should.be.false;
+
+ var selection = {
+ '_id': 0
+ }
+
+ doc = new TestDocument(undefined, selection);
+ doc.init({
+ test : 'test'
+ , numbers : [4,5,6,7]
+ , nested : {
+ age : 5
+ , cool : DocumentObjectId.fromString('4c6c2d6240ced95d0e00003c')
+ , path : 'my path'
+ , deep : { x: 'a string' }
+ }
+ , notapath: 'i am not in the schema'
+ });
+
+ doc.isSelected('_id').should.be.false;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+
+ doc = new TestDocument({ test: 'boom' });
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.true;
+ doc.isSelected('numbers').should.be.true;
+ doc.isSelected('oids').should.be.true;
+ doc.isSelected('nested').should.be.true;
+ doc.isSelected('nested.age').should.be.true;
+ doc.isSelected('nested.cool').should.be.true;
+ doc.isSelected('nested.path').should.be.true;
+ doc.isSelected('nested.deep').should.be.true;
+ doc.isSelected('nested.nope').should.be.true;
+ doc.isSelected('nested.deep.x').should.be.true;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+ doc.isSelected('nested.deep.y').should.be.true;
+ doc.isSelected('noway').should.be.true;
+ doc.isSelected('notapath').should.be.true;
+ doc.isSelected('em').should.be.true;
+ doc.isSelected('em.title').should.be.true;
+ doc.isSelected('em.body').should.be.true;
+ doc.isSelected('em.nonpath').should.be.true;
+
+ doc = new TestDocument({ test: 'boom' }, true);
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.true;
+ doc.isSelected('numbers').should.be.true;
+ doc.isSelected('oids').should.be.true;
+ doc.isSelected('nested').should.be.true;
+ doc.isSelected('nested.age').should.be.true;
+ doc.isSelected('nested.cool').should.be.true;
+ doc.isSelected('nested.path').should.be.true;
+ doc.isSelected('nested.deep').should.be.true;
+ doc.isSelected('nested.nope').should.be.true;
+ doc.isSelected('nested.deep.x').should.be.true;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+ doc.isSelected('nested.deep.y').should.be.true;
+ doc.isSelected('noway').should.be.true;
+ doc.isSelected('notapath').should.be.true;
+ doc.isSelected('em').should.be.true;
+ doc.isSelected('em.title').should.be.true;
+ doc.isSelected('em.body').should.be.true;
+ doc.isSelected('em.nonpath').should.be.true;
+ },
+
+ 'unselected required fields should pass validation': function () {
+ var db = start()
+ , Tschema = new Schema({ name: String, req: { type: String, required: true }})
+ , T = db.model('unselectedRequiredFieldValidation', Tschema);
+
+ var t = new T({ name: 'teeee', req: 'i am required' });
+ t.save(function (err) {
+ should.strictEqual(null, err);
+ T.findById(t).select('name').exec(function (err, t) {
+ should.strictEqual(null, err);
+ should.strictEqual(undefined, t.req);
+ t.name = 'wooo';
+ t.save(function (err) {
+ should.strictEqual(null, err);
+
+ T.findById(t).select('name').exec(function (err, t) {
+ should.strictEqual(null, err);
+ t.req = undefined;
+ t.save(function (err) {
+ err = String(err);
+ var invalid = /Validator "required" failed for path req/.test(err);
+ invalid.should.be.true;
+ t.req = 'it works again'
+ t.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ });
+ });
+ });
+ }
+};
diff --git a/node_modules/mongoose/test/drivers/node-mongodb-native/collection.test.js b/node_modules/mongoose/test/drivers/node-mongodb-native/collection.test.js
new file mode 100644
index 0000000..2b3d13d
--- /dev/null
+++ b/node_modules/mongoose/test/drivers/node-mongodb-native/collection.test.js
@@ -0,0 +1,63 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('../../common')
+ , mongoose = start.mongoose
+ , should = require('should')
+ , Schema = mongoose.Schema;
+
+/**
+ * Setup.
+ */
+
+mongoose.model('NativeDriverTest', new Schema({
+ title: String
+}));
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that trying to implement a sparse index works': function () {
+ var db = start()
+ , NativeTestCollection = db.model('NativeDriverTest');
+
+ NativeTestCollection.collection.ensureIndex({ title: 1 }, { sparse: true }, function (err) {
+ should.strictEqual(!!err, false);
+ NativeTestCollection.collection.getIndexes(function (err, indexes) {
+ db.close();
+ should.strictEqual(!!err, false);
+ indexes.should.be.instanceof(Object);
+ indexes['title_1'].should.eql([['title', 1]]);
+ });
+ });
+ },
+
+ 'test that the -native traditional ensureIndex spec syntax for fields works': function () {
+ var db = start()
+ , NativeTestCollection = db.model('NativeDriverTest');
+
+ NativeTestCollection.collection.ensureIndex([['a', 1]], function () {
+ db.close();
+ });
+ },
+
+ 'unique index fails passes error': function () {
+ var db = start()
+ , schema = new Schema({ title: String })
+ , NativeTestCollection = db.model('NativeDriverTestUnique', schema)
+
+ NativeTestCollection.create({ title: 'x' }, {title:'x'}, function (err) {
+ should.strictEqual(!!err, false);
+
+ NativeTestCollection.collection.ensureIndex({ title: 1 }, { unique: true, safe: true }, function (err) {
+ db.close();
+ ;/E11000 duplicate key error index/.test(err.message).should.equal(true);
+ });
+ });
+ }
+};
diff --git a/node_modules/mongoose/test/dropdb.js b/node_modules/mongoose/test/dropdb.js
new file mode 100755
index 0000000..0b806e7
--- /dev/null
+++ b/node_modules/mongoose/test/dropdb.js
@@ -0,0 +1,7 @@
+var start = require('./common')
+var db = start();
+db.on('open', function () {
+ db.db.dropDatabase(function () {
+ process.exit();
+ });
+});
diff --git a/node_modules/mongoose/test/index.test.js b/node_modules/mongoose/test/index.test.js
new file mode 100644
index 0000000..db0625d
--- /dev/null
+++ b/node_modules/mongoose/test/index.test.js
@@ -0,0 +1,235 @@
+
+var url = require('url')
+ , start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , Mongoose = mongoose.Mongoose
+ , Schema = mongoose.Schema;
+
+module.exports = {
+
+ 'test connecting to the demo database': function(beforeExit){
+ var db = start()
+ , connected = false;
+
+ db.on('open', function(){
+ connected = true;
+ db.close();
+ });
+
+ beforeExit(function(){
+ connected.should.be.true;
+ });
+ },
+
+ 'test default connection': function(beforeExit){
+ var db = mongoose.connection
+ , uri = 'mongodb://localhost/mongoose_test'
+ , connected = false;
+
+ mongoose.connect(process.env.MONGOOSE_TEST_URI || uri);
+ db.on('open', function(){
+ connected = true;
+ db.close();
+ });
+
+ beforeExit(function(){
+ connected.should.be.true;
+ });
+ },
+
+ 'test setting options': function(){
+ var mongoose = new Mongoose();
+
+ mongoose.set('a', 'b');
+ mongoose.set('long option', 'c');
+
+ mongoose.get('a').should.eql('b');
+ mongoose.set('a').should.eql('b');
+ mongoose.get('long option').should.eql('c');
+ },
+
+ 'test declaring global plugins': function (beforeExit) {
+ var mong = new Mongoose()
+ , schema = new Schema()
+ , called = 0;
+
+ mong.plugin(function (s) {
+ s.should.equal(schema);
+ called++;
+ });
+
+ schema.plugin(function (s) {
+ s.should.equal(schema);
+ called++;
+ });
+
+ mong.model('GlobalPlugins', schema);
+
+ beforeExit(function () {
+ called.should.eql(2);
+ });
+ },
+
+ 'test disconnection of all connections': function (beforeExit) {
+ var mong = new Mongoose()
+ , uri = 'mongodb://localhost/mongoose_test'
+ , connections = 0
+ , disconnections = 0;
+
+ mong.connect(process.env.MONGOOSE_TEST_URI || uri);
+ var db = mong.connection;
+
+ db.on('open', function(){
+ connections++;
+ });
+
+ db.on('close', function () {
+ disconnections++;
+ });
+
+ var db2 = mong.createConnection(process.env.MONGOOSE_TEST_URI || uri);
+
+ db2.on('open', function () {
+ connections++;
+ });
+
+ db2.on('close', function () {
+ disconnections++;
+ });
+
+ mong.disconnect();
+
+ beforeExit(function () {
+ connections.should.eql(2);
+ disconnections.should.eql(2);
+ });
+ },
+
+ 'test disconnection of all connections callback': function (beforeExit) {
+ var mong = new Mongoose()
+ , uri = 'mongodb://localhost/mongoose_test'
+ , called = false;
+
+ mong.connect(process.env.MONGOOSE_TEST_URI || uri);
+
+ mong.connection.on('open', function () {
+ mong.disconnect(function () {
+ called = true;
+ });
+ });
+
+ beforeExit(function () {
+ called.should.be.true;
+ });
+ },
+
+ 'try accessing a model that hasn\'t been defined': function () {
+ var mong = new Mongoose()
+ , thrown = false;
+
+ try {
+ mong.model('Test');
+ } catch (e) {
+ /hasn't been registered/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ },
+
+ 'test connecting with a signature of host, database, function': function (){
+ var mong = new Mongoose()
+ , uri = process.env.MONGOOSE_TEST_URI || 'mongodb://localhost/mongoose_test';
+
+ uri = url.parse(uri);
+
+ mong.connect(uri.hostname, uri.pathname.substr(1), function (err) {
+ should.strictEqual(err, null);
+ mong.connection.close();
+ });
+ },
+
+ 'test connecting to a replica set': function () {
+ var uri = process.env.MONGOOSE_SET_TEST_URI;
+
+ if (!uri) {
+ console.log('\033[30m', '\n', 'You\'re not testing replica sets!'
+ , '\n', 'Please set the MONGOOSE_SET_TEST_URI env variable.', '\n'
+ , 'e.g: `mongodb://localhost:27017/db,mongodb://localhost…`', '\n'
+ , '\033[39m');
+ return;
+ }
+
+ var mong = new Mongoose();
+
+ mong.connectSet(uri, function (err) {
+ should.strictEqual(err, null);
+
+ mong.model('Test', new mongoose.Schema({
+ test: String
+ }));
+
+ var Test = mong.model('Test')
+ , test = new Test();
+
+ test.test = 'aa';
+ test.save(function (err) {
+ should.strictEqual(err, null);
+
+ Test.findById(test._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.test.should.eql('aa');
+
+ mong.connection.close();
+ });
+ });
+ });
+ },
+
+ 'test initializing a new Connection to a replica set': function () {
+ var uri = process.env.MONGOOSE_SET_TEST_URI;
+
+ if (!uri) return;
+
+ var mong = new Mongoose(true);
+
+ var conn = mong.createSetConnection(uri, function (err) {
+ should.strictEqual(err, null);
+
+ mong.model('ReplSetTwo', new mongoose.Schema({
+ test: String
+ }));
+
+ var Test = conn.model('ReplSetTwo')
+ , test = new Test();
+
+ test.test = 'aa';
+ test.save(function (err) {
+ should.strictEqual(err, null);
+
+ Test.findById(test._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.test.should.eql('aa');
+
+ conn.close();
+ });
+ });
+ });
+ },
+
+ 'test public exports': function () {
+ mongoose.version.should.be.a('string');
+ mongoose.Collection.should.be.a('function');
+ mongoose.Connection.should.be.a('function');
+ mongoose.Schema.should.be.a('function');
+ mongoose.SchemaType.should.be.a('function');
+ mongoose.Query.should.be.a('function');
+ mongoose.Promise.should.be.a('function');
+ mongoose.Model.should.be.a('function');
+ mongoose.Document.should.be.a('function');
+ }
+
+};
diff --git a/node_modules/mongoose/test/model.querying.test.js b/node_modules/mongoose/test/model.querying.test.js
new file mode 100644
index 0000000..2f5d53e
--- /dev/null
+++ b/node_modules/mongoose/test/model.querying.test.js
@@ -0,0 +1,2352 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Query = require('../lib/query')
+ , Schema = mongoose.Schema
+ , SchemaType = mongoose.SchemaType
+ , CastError = SchemaType.CastError
+ , ObjectId = Schema.ObjectId
+ , MongooseBuffer = mongoose.Types.Buffer
+ , DocumentObjectId = mongoose.Types.ObjectId;
+
+/**
+ * Setup.
+ */
+
+var Comments = new Schema();
+
+Comments.add({
+ title : String
+ , date : Date
+ , body : String
+ , comments : [Comments]
+});
+
+var BlogPostB = new Schema({
+ title : String
+ , author : String
+ , slug : String
+ , date : Date
+ , meta : {
+ date : Date
+ , visitors : Number
+ }
+ , published : Boolean
+ , mixed : {}
+ , numbers : [Number]
+ , tags : [String]
+ , sigs : [Buffer]
+ , owners : [ObjectId]
+ , comments : [Comments]
+ , def : { type: String, default: 'kandinsky' }
+});
+
+mongoose.model('BlogPostB', BlogPostB);
+var collection = 'blogposts_' + random();
+
+var ModSchema = new Schema({
+ num: Number
+});
+mongoose.model('Mod', ModSchema);
+
+var geoSchema = new Schema({ loc: { type: [Number], index: '2d'}});
+
+module.exports = {
+
+ 'test that find returns a Query': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ // query
+ BlogPostB.find({}).should.be.an.instanceof(Query);
+
+ // query, fields
+ BlogPostB.find({}, {}).should.be.an.instanceof(Query);
+
+ // query, fields (array)
+ BlogPostB.find({}, []).should.be.an.instanceof(Query);
+
+ // query, fields, options
+ BlogPostB.find({}, {}, {}).should.be.an.instanceof(Query);
+
+ // query, fields (array), options
+ BlogPostB.find({}, [], {}).should.be.an.instanceof(Query);
+
+ db.close();
+ },
+
+ 'test that findOne returns a Query': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ // query
+ BlogPostB.findOne({}).should.be.an.instanceof(Query);
+
+ // query, fields
+ BlogPostB.findOne({}, {}).should.be.an.instanceof(Query);
+
+ // query, fields (array)
+ BlogPostB.findOne({}, []).should.be.an.instanceof(Query);
+
+ // query, fields, options
+ BlogPostB.findOne({}, {}, {}).should.be.an.instanceof(Query);
+
+ // query, fields (array), options
+ BlogPostB.findOne({}, [], {}).should.be.an.instanceof(Query);
+
+ db.close();
+ },
+
+ 'test that an empty find does not hang': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+
+ function fn () {
+ db.close();
+ };
+
+ BlogPostB.find({}, fn);
+ },
+
+ 'test that a query is executed when a callback is passed': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , count = 5
+ , q = { _id: new DocumentObjectId }; // make sure the query is fast
+
+ function fn () {
+ --count || db.close();
+ };
+
+ // query
+ BlogPostB.find(q, fn).should.be.an.instanceof(Query);
+
+ // query, fields
+ BlogPostB.find(q, {}, fn).should.be.an.instanceof(Query);
+
+ // query, fields (array)
+ BlogPostB.find(q, [], fn).should.be.an.instanceof(Query);
+
+ // query, fields, options
+ BlogPostB.find(q, {}, {}, fn).should.be.an.instanceof(Query);
+
+ // query, fields (array), options
+ BlogPostB.find(q, [], {}, fn).should.be.an.instanceof(Query);
+ },
+
+ 'test that query is executed where a callback for findOne': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , count = 5
+ , q = { _id: new DocumentObjectId }; // make sure the query is fast
+
+ function fn () {
+ --count || db.close();
+ };
+
+ // query
+ BlogPostB.findOne(q, fn).should.be.an.instanceof(Query);
+
+ // query, fields
+ BlogPostB.findOne(q, {}, fn).should.be.an.instanceof(Query);
+
+ // query, fields (array)
+ BlogPostB.findOne(q, [], fn).should.be.an.instanceof(Query);
+
+ // query, fields, options
+ BlogPostB.findOne(q, {}, {}, fn).should.be.an.instanceof(Query);
+
+ // query, fields (array), options
+ BlogPostB.findOne(q, [], {}, fn).should.be.an.instanceof(Query);
+ },
+
+ 'test that count returns a Query': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.count({}).should.be.an.instanceof(Query);
+
+ db.close();
+ },
+
+ 'test that count Query executes when you pass a callback': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , pending = 2
+
+ function fn () {
+ if (--pending) return;
+ db.close();
+ };
+
+ BlogPostB.count({}, fn).should.be.an.instanceof(Query);
+ BlogPostB.count(fn).should.be.an.instanceof(Query);
+ },
+
+ 'test that distinct returns a Query': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.distinct('title', {}).should.be.an.instanceof(Query);
+
+ db.close();
+ },
+
+ 'test that distinct Query executes when you pass a callback': function () {
+ var db = start();
+ var Address = new Schema({ zip: String });
+ Address = db.model('Address', Address, 'addresses_' + random());
+
+ Address.create({ zip: '10010'}, { zip: '10010'}, { zip: '99701'}, function (err, a1, a2, a3) {
+ should.strictEqual(null, err);
+ var query = Address.distinct('zip', {}, function (err, results) {
+ should.strictEqual(null, err);
+ results.should.eql(['10010', '99701']);
+ db.close();
+ });
+ query.should.be.an.instanceof(Query);
+ });
+ },
+
+
+ 'test that update returns a Query': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.update({}, {}).should.be.an.instanceof(Query);
+ BlogPostB.update({}, {}, {}).should.be.an.instanceof(Query);
+
+ db.close();
+ },
+
+ 'test that update Query executes when you pass a callback': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , count = 2;
+
+ function fn () {
+ --count || db.close();
+ };
+
+ BlogPostB.update({title: random()}, {}, fn).should.be.an.instanceof(Query);
+
+ BlogPostB.update({title: random()}, {}, {}, fn).should.be.an.instanceof(Query);
+ },
+
+ 'test finding a document': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , title = 'Wooooot ' + random();
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ title: title }, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.get('title').should.eql(title);
+ doc.isNew.should.be.false;
+
+ db.close();
+ });
+ });
+ },
+
+ 'test finding a document byId': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , title = 'Edwald ' + random();
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ var pending = 2;
+
+ BlogPostB.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+ doc.should.be.an.instanceof(BlogPostB);
+ doc.get('title').should.eql(title);
+ --pending || db.close();
+ });
+
+ BlogPostB.findById(post.get('_id').toHexString(), function (err, doc) {
+ should.strictEqual(err, null);
+ doc.should.be.an.instanceof(BlogPostB);
+ doc.get('title').should.eql(title);
+ --pending || db.close();
+ });
+ });
+ },
+
+ 'test finding documents': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , title = 'Wooooot ' + random();
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.find({ title: title }, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.should.have.length(2);
+
+ docs[0].get('title').should.eql(title);
+ docs[0].isNew.should.be.false;
+
+ docs[1].get('title').should.eql(title);
+ docs[1].isNew.should.be.false;
+
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test finding documents where an array that contains one specific member': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+ BlogPostB.create({numbers: [100, 101, 102]}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.find({numbers: 100}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(created._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test counting documents': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , title = 'Wooooot ' + random();
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.count({ title: title }, function (err, count) {
+ should.strictEqual(err, null);
+
+ count.should.be.a('number');
+ count.should.eql(2);
+
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test query casting': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , title = 'Loki ' + random();
+
+ var post = new BlogPostB()
+ , id = DocumentObjectId.toString(post.get('_id'));
+
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ _id: id }, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.get('title').should.equal(title);
+ db.close();
+ });
+ });
+ },
+
+ 'test a query that includes a casting error': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.find({ date: 'invalid date' }, function (err) {
+ err.should.be.an.instanceof(Error);
+ err.should.be.an.instanceof(CastError);
+ db.close();
+ });
+ },
+
+ 'test findOne queries that require casting for $modifiers': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , post = new BlogPostB({
+ meta: {
+ visitors: -10
+ }
+ });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ 'meta.visitors': { $gt: '-20', $lt: -1 } },
+ function (err, found) {
+ found.get('meta.visitors')
+ .valueOf().should.equal(post.get('meta.visitors').valueOf());
+ found.id;
+ found.get('_id').should.eql(post.get('_id'));
+ db.close();
+ });
+ });
+ },
+
+ 'test find queries that require casting for $modifiers': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , post = new BlogPostB({
+ meta: {
+ visitors: -75
+ }
+ });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.find({ 'meta.visitors': { $gt: '-100', $lt: -50 } },
+ function (err, found) {
+ should.strictEqual(err, null);
+
+ found.should.have.length(1);
+ found[0].get('_id').should.eql(post.get('_id'));
+ found[0].get('meta.visitors').valueOf()
+ .should.equal(post.get('meta.visitors').valueOf());
+ db.close();
+ });
+ });
+ },
+
+ // GH-199
+ 'test find queries where $in cast the values wherein the array': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ var post = new BlogPostB()
+ , id = DocumentObjectId.toString(post._id);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ _id: { $in: [id] } }, function (err, doc) {
+ should.strictEqual(err, null);
+
+ DocumentObjectId.toString(doc._id).should.eql(id);
+ db.close();
+ });
+ });
+ },
+
+ // GH-232
+ 'test find queries where $nin cast the values wherein the array': function () {
+ var db = start()
+ , NinSchema = new Schema({
+ num: Number
+ });
+ mongoose.model('Nin', NinSchema);
+ var Nin = db.model('Nin', 'nins_' + random());
+ Nin.create({ num: 1 }, function (err, one) {
+ should.strictEqual(err, null);
+ Nin.create({ num: 2 }, function (err, two) {
+ should.strictEqual(err, null);
+ Nin.create({num: 3}, function (err, three) {
+ should.strictEqual(err, null);
+ Nin.find({ num: {$nin: [2]}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test find queries with $ne with single value against array': function () {
+ var db = start();
+ var schema = new Schema({
+ ids: [Schema.ObjectId]
+ , b: Schema.ObjectId
+ });
+
+ var NE = db.model('NE_Test', schema, 'nes__' + random());
+
+ var id1 = new DocumentObjectId;
+ var id2 = new DocumentObjectId;
+ var id3 = new DocumentObjectId;
+ var id4 = new DocumentObjectId;
+
+ NE.create({ ids: [id1, id4], b: id3 }, function (err, ne1) {
+ should.strictEqual(err, null);
+ NE.create({ ids: [id2, id4], b: id3 },function (err, ne2) {
+ should.strictEqual(err, null);
+
+ var query = NE.find({ 'b': id3.toString(), 'ids': { $ne: id1 }});
+ query.run(function (err, nes1) {
+ should.strictEqual(err, null);
+ nes1.length.should.eql(1);
+
+ NE.find({ b: { $ne: [1] }}, function (err, nes2) {
+ err.message.should.eql("Invalid ObjectId");
+
+ NE.find({ b: { $ne: 4 }}, function (err, nes3) {
+ err.message.should.eql("Invalid ObjectId");
+
+ NE.find({ b: id3, ids: { $ne: id4 }}, function (err, nes4) {
+ db.close();
+ should.strictEqual(err, null);
+ nes4.length.should.eql(0);
+ });
+ });
+ });
+ });
+
+ });
+ });
+
+ },
+
+ 'test for findById where partial initialization': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , queries = 5;
+
+ var post = new BlogPostB();
+
+ post.title = 'hahaha';
+ post.slug = 'woot';
+ post.meta.visitors = 53;
+ post.tags = ['humidity', 'soggy'];
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+ doc.isInit('title').should.be.true;
+ doc.isInit('slug').should.be.true;
+ doc.isInit('date').should.be.false;
+ doc.isInit('meta.visitors').should.be.true;
+ doc.meta.visitors.valueOf().should.equal(53);
+ doc.tags.length.should.equal(2);
+ --queries || db.close();
+ });
+
+ BlogPostB.findById(post.get('_id'), ['title'], function (err, doc) {
+ should.strictEqual(err, null);
+ doc.isInit('title').should.be.true;
+ doc.isInit('slug').should.be.false;
+ doc.isInit('date').should.be.false;
+ doc.isInit('meta.visitors').should.be.false;
+ should.strictEqual(undefined, doc.meta.visitors);
+ should.strictEqual(undefined, doc.tags);
+ --queries || db.close();
+ });
+
+ BlogPostB.findById(post.get('_id'), { slug: 0 }, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.isInit('title').should.be.true;
+ doc.isInit('slug').should.be.false;
+ doc.isInit('date').should.be.false;
+ doc.isInit('meta.visitors').should.be.true;
+ doc.meta.visitors.valueOf().should.equal(53);
+ doc.tags.length.should.equal(2);
+ --queries || db.close();
+ });
+
+ BlogPostB.findById(post.get('_id'), { title:1 }, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.isInit('title').should.be.true;
+ doc.isInit('slug').should.be.false;
+ doc.isInit('date').should.be.false;
+ doc.isInit('meta.visitors').should.be.false;
+ should.strictEqual(undefined, doc.meta.visitors);
+ should.strictEqual(undefined, doc.tags);
+ --queries || db.close();
+ });
+
+ BlogPostB.findById(post.get('_id'), ['slug'], function (err, doc) {
+ should.strictEqual(err, null);
+ doc.isInit('title').should.be.false;
+ doc.isInit('slug').should.be.true;
+ doc.isInit('date').should.be.false;
+ doc.isInit('meta.visitors').should.be.false;
+ should.strictEqual(undefined, doc.meta.visitors);
+ should.strictEqual(undefined, doc.tags);
+ --queries || db.close();
+ });
+ });
+ },
+
+ 'findOne where subset of fields excludes _id': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+ BlogPostB.create({title: 'subset 1'}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.findOne({title: 'subset 1'}, {title: 1, _id: 0}, function (err, found) {
+ should.strictEqual(err, null);
+ should.strictEqual(undefined, found._id);
+ found.title.should.equal('subset 1');
+ db.close();
+ });
+ });
+ },
+
+ 'test find where subset of fields, excluding _id': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+ BlogPostB.create({title: 'subset 1', author: 'me'}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.find({title: 'subset 1'}, {title: 1, _id: 0}, function (err, found) {
+ should.strictEqual(err, null);
+ should.strictEqual(undefined, found[0]._id);
+ found[0].title.should.equal('subset 1');
+ should.strictEqual(undefined, found[0].def);
+ should.strictEqual(undefined, found[0].author);
+ should.strictEqual(false, Array.isArray(found[0].comments));
+ db.close();
+ });
+ });
+ },
+
+ // gh-541
+ 'find subset of fields excluding embedded doc _id': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: 'LOTR', comments: [{ title: ':)' }]}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.find({_id: created}, { _id: 0, 'comments._id': 0 }, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(undefined, found[0]._id);
+ found[0].title.should.equal('LOTR');
+ should.strictEqual('kandinsky', found[0].def);
+ should.strictEqual(undefined, found[0].author);
+ should.strictEqual(true, Array.isArray(found[0].comments));
+ found[0].comments.length.should.equal(1);
+ found[0].comments[0].title.should.equal(':)');
+ should.strictEqual(undefined, found[0].comments[0]._id);
+ // gh-590
+ should.strictEqual(null, found[0].comments[0].id);
+ });
+ });
+ },
+
+
+ 'exluded fields should be undefined': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , date = new Date
+
+ BlogPostB.create({title: 'subset 1', author: 'me', meta: { date: date }}, function (err, created) {
+ should.strictEqual(err, null);
+ var id = created.id;
+ BlogPostB.findById(created.id, {title: 0, 'meta.date': 0, owners: 0}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ should.strictEqual(undefined, found.title);
+ should.strictEqual('kandinsky', found.def);
+ should.strictEqual('me', found.author);
+ should.strictEqual(true, Array.isArray(found.comments));
+ should.equal(undefined, found.meta.date);
+ found.comments.length.should.equal(0);
+ should.equal(undefined, found.owners);
+ });
+ });
+ },
+
+ 'exluded fields should be undefined and defaults applied to other fields': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , id = new DocumentObjectId
+ , date = new Date
+
+ BlogPostB.collection.insert({ _id: id, title: 'hahaha1', meta: { date: date }}, function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findById(id, {title: 0}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found._id.should.eql(id);
+ should.strictEqual(undefined, found.title);
+ should.strictEqual('kandinsky', found.def);
+ should.strictEqual(undefined, found.author);
+ should.strictEqual(true, Array.isArray(found.comments));
+ should.equal(date.toString(), found.meta.date.toString());
+ found.comments.length.should.equal(0);
+ });
+ });
+ },
+
+ 'test for find where partial initialization': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , queries = 4;
+
+ var post = new BlogPostB();
+
+ post.title = 'hahaha';
+ post.slug = 'woot';
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.find({ _id: post.get('_id') }, function (err, docs) {
+ should.strictEqual(err, null);
+ docs[0].isInit('title').should.be.true;
+ docs[0].isInit('slug').should.be.true;
+ docs[0].isInit('date').should.be.false;
+ should.strictEqual('kandinsky', docs[0].def);
+ --queries || db.close();
+ });
+
+ BlogPostB.find({ _id: post.get('_id') }, ['title'], function (err, docs) {
+ should.strictEqual(err, null);
+ docs[0].isInit('title').should.be.true;
+ docs[0].isInit('slug').should.be.false;
+ docs[0].isInit('date').should.be.false;
+ should.strictEqual(undefined, docs[0].def);
+ --queries || db.close();
+ });
+
+ BlogPostB.find({ _id: post.get('_id') }, { slug: 0, def: 0 }, function (err, docs) {
+ should.strictEqual(err, null);
+ docs[0].isInit('title').should.be.true;
+ docs[0].isInit('slug').should.be.false;
+ docs[0].isInit('date').should.be.false;
+ should.strictEqual(undefined, docs[0].def);
+ --queries || db.close();
+ });
+
+ BlogPostB.find({ _id: post.get('_id') }, ['slug'], function (err, docs) {
+ should.strictEqual(err, null);
+ docs[0].isInit('title').should.be.false;
+ docs[0].isInit('slug').should.be.true;
+ docs[0].isInit('date').should.be.false;
+ should.strictEqual(undefined, docs[0].def);
+ --queries || db.close();
+ });
+ });
+ },
+
+ // GH-204
+ 'test query casting when finding by Date': function () {
+ var db = start()
+ , P = db.model('BlogPostB', collection);
+
+ var post = new P;
+
+ post.meta.date = new Date();
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ P.findOne({ _id: post._id, 'meta.date': { $lte: Date.now() } }, function (err, doc) {
+ should.strictEqual(err, null);
+
+ DocumentObjectId.toString(doc._id).should.eql(DocumentObjectId.toString(post._id));
+ doc.meta.date = null;
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+ P.findById(doc._id, function (err, doc) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(doc.meta.date, null);
+ });
+ });
+ });
+ });
+ },
+
+ // gh-523
+ 'null boolean default is allowed': function () {
+ var db = start()
+ , s1 = new Schema({ b: { type: Boolean, default: null }})
+ , M1 = db.model('NullDateDefaultIsAllowed1', s1)
+ , s2 = new Schema({ b: { type: Boolean, default: false }})
+ , M2 = db.model('NullDateDefaultIsAllowed2', s2)
+ , s3 = new Schema({ b: { type: Boolean, default: true }})
+ , M3 = db.model('NullDateDefaultIsAllowed3', s3)
+
+ db.close();
+
+ var m1 = new M1;
+ should.strictEqual(null, m1.b);
+ var m2 = new M2;
+ should.strictEqual(false, m2.b);
+ var m3 = new M3;
+ should.strictEqual(true, m3.b);
+ },
+
+ // GH-220
+ 'test querying if an array contains at least a certain single member': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ var post = new BlogPostB();
+
+ post.tags.push('cat');
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({tags: 'cat'}, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.id;
+ doc._id.should.eql(post._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test querying if an array contains one of multiple members $in a set': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ var post = new BlogPostB();
+
+ post.tags.push('football');
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({tags: {$in: ['football', 'baseball']}}, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.id;
+ doc._id.should.eql(post._id);
+
+ BlogPostB.findOne({ _id: post._id, tags: /otba/i }, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.id;
+ doc._id.should.eql(post._id);
+
+ db.close();
+ })
+ });
+ });
+ },
+
+ 'test querying if an array contains one of multiple members $in a set 2': function () {
+ var db = start()
+ , BlogPostA = db.model('BlogPostB', collection)
+
+ var post = new BlogPostA({ tags: ['gooberOne'] });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ var query = {tags: {$in:[ 'gooberOne' ]}};
+
+ BlogPostA.findOne(query, function (err, returned) {
+ done();
+ should.strictEqual(err, null);
+ ;(!!~returned.tags.indexOf('gooberOne')).should.be.true;
+ returned.id;
+ returned._id.should.eql(post._id);
+ });
+ });
+
+ post.collection.insert({ meta: { visitors: 9898, a: null } }, {}, function (err, b) {
+ should.strictEqual(err, null);
+
+ BlogPostA.findOne({_id: b[0]._id}, function (err, found) {
+ done();
+ should.strictEqual(err, null);
+ found.get('meta.visitors').valueOf().should.eql(9898);
+ })
+ });
+
+ var pending = 2;
+ function done () {
+ if (--pending) return;
+ db.close();
+ }
+ },
+
+ 'test querying via $where a string': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({ title: 'Steve Jobs', author: 'Steve Jobs'}, function (err, created) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ $where: "this.title && this.title === this.author" }, function (err, found) {
+ should.strictEqual(err, null);
+
+ found.id;
+ found._id.should.eql(created._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test querying via $where a function': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({ author: 'Atari', slug: 'Atari'}, function (err, created) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ $where: function () {
+ return (this.author && this.slug && this.author === this.slug);
+ } }, function (err, found) {
+ should.strictEqual(err, null);
+
+ found.id;
+ found._id.should.eql(created._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test find where $exists': function () {
+ var db = start()
+ , ExistsSchema = new Schema({
+ a: Number
+ , b: String
+ });
+ mongoose.model('Exists', ExistsSchema);
+ var Exists = db.model('Exists', 'exists_' + random());
+ Exists.create({ a: 1}, function (err, aExisting) {
+ should.strictEqual(err, null);
+ Exists.create({b: 'hi'}, function (err, bExisting) {
+ should.strictEqual(err, null);
+ Exists.find({b: {$exists: true}}, function (err, docs) {
+ should.strictEqual(err, null);
+ db.close();
+ docs.should.have.length(1);
+ });
+ });
+ });
+ },
+
+ 'test finding based on nested fields': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , post = new BlogPostB({
+ meta: {
+ visitors: 5678
+ }
+ });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ 'meta.visitors': 5678 }, function (err, found) {
+ should.strictEqual(err, null);
+ found.get('meta.visitors')
+ .valueOf().should.equal(post.get('meta.visitors').valueOf());
+ found.id;
+ found.get('_id').should.eql(post.get('_id'));
+ db.close();
+ });
+ });
+ },
+
+ // GH-242
+ 'test finding based on embedded document fields': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({comments: [{title: 'i should be queryable'}], numbers: [1,2,33333], tags:['yes', 'no']}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.findOne({'comments.title': 'i should be queryable'}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ BlogPostB.findOne({'comments.0.title': 'i should be queryable'}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ // GH-463
+ BlogPostB.findOne({'numbers.2': 33333}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ BlogPostB.findOne({'tags.1': 'no'}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ db.close();
+ });
+ });
+ });
+ });
+ });
+ },
+
+ // GH-389
+ 'find nested doc using string id': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({comments: [{title: 'i should be queryable by _id'}, {title:'me too me too!'}]}, function (err, created) {
+ should.strictEqual(err, null);
+ var id = created.comments[1]._id.toString();
+ BlogPostB.findOne({'comments._id': id}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(!! found, true, 'Find by nested doc id hex string fails');
+ found.id;
+ found._id.should.eql(created._id);
+ });
+ });
+ },
+
+ 'test finding where $elemMatch': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , dateAnchor = +new Date;
+
+ BlogPostB.create({comments: [{title: 'elemMatch', date: dateAnchor + 5}]}, function (err, createdAfter) {
+ should.strictEqual(err, null);
+ BlogPostB.create({comments: [{title: 'elemMatch', date: dateAnchor - 5}]}, function (err, createdBefore) {
+ should.strictEqual(err, null);
+ BlogPostB.find({'comments': {'$elemMatch': {title: 'elemMatch', date: {$gt: dateAnchor}}}},
+ function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(createdAfter._id);
+ db.close();
+ }
+ );
+ });
+ });
+ },
+
+ 'test finding where $mod': function () {
+ var db = start()
+ , Mod = db.model('Mod', 'mods_' + random());
+ Mod.create({num: 1}, function (err, one) {
+ should.strictEqual(err, null);
+ Mod.create({num: 2}, function (err, two) {
+ should.strictEqual(err, null);
+ Mod.find({num: {$mod: [2, 1]}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(one._id);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test finding where $not': function () {
+ var db = start()
+ , Mod = db.model('Mod', 'mods_' + random());
+ Mod.create({num: 1}, function (err, one) {
+ should.strictEqual(err, null);
+ Mod.create({num: 2}, function (err, two) {
+ should.strictEqual(err, null);
+ Mod.find({num: {$not: {$mod: [2, 1]}}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(two._id);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test finding where $or': function () {
+ var db = start()
+ , Mod = db.model('Mod', 'mods_' + random());
+
+ Mod.create({num: 1}, {num: 2, str: 'two'}, function (err, one, two) {
+ should.strictEqual(err, null);
+
+ var pending = 3;
+ test1();
+ test2();
+ test3();
+
+ function test1 () {
+ Mod.find({$or: [{num: 1}, {num: 2}]}, function (err, found) {
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ found[0]._id.should.eql(one._id);
+ found[1]._id.should.eql(two._id);
+ });
+ }
+
+ function test2 () {
+ Mod.find({ $or: [{ str: 'two'}, {str:'three'}] }, function (err, found) {
+ if (err) console.error(err);
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(two._id);
+ });
+ }
+
+ function test3 () {
+ Mod.find({$or: [{num: 1}]}).$or([{ str: 'two' }]).run(function (err, found) {
+ if (err) console.error(err);
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ found[0]._id.should.eql(one._id);
+ found[1]._id.should.eql(two._id);
+ });
+ }
+
+ function done () {
+ if (--pending) return;
+ db.close();
+ }
+ });
+ },
+
+ 'finding where #nor': function () {
+ var db = start()
+ , Mod = db.model('Mod', 'nor_' + random());
+
+ Mod.create({num: 1}, {num: 2, str: 'two'}, function (err, one, two) {
+ should.strictEqual(err, null);
+
+ var pending = 3;
+ test1();
+ test2();
+ test3();
+
+ function test1 () {
+ Mod.find({$nor: [{num: 1}, {num: 3}]}, function (err, found) {
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(two._id);
+ });
+ }
+
+ function test2 () {
+ Mod.find({ $nor: [{ str: 'two'}, {str:'three'}] }, function (err, found) {
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(one._id);
+ });
+ }
+
+ function test3 () {
+ Mod.find({$nor: [{num: 2}]}).$nor([{ str: 'two' }]).run(function (err, found) {
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(one._id);
+ });
+ }
+
+ function done () {
+ if (--pending) return;
+ db.close();
+ }
+ });
+ },
+
+ 'test finding where $ne': function () {
+ var db = start()
+ , Mod = db.model('Mod', 'mods_' + random());
+ Mod.create({num: 1}, function (err, one) {
+ should.strictEqual(err, null);
+ Mod.create({num: 2}, function (err, two) {
+ should.strictEqual(err, null);
+ Mod.create({num: 3}, function (err, three) {
+ should.strictEqual(err, null);
+ Mod.find({num: {$ne: 1}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ found[0]._id.should.eql(two._id);
+ found[1]._id.should.eql(three._id);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding null matches null and undefined': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection + random());
+
+ BlogPostB.create(
+ { title: 'A', author: null }
+ , { title: 'B' }, function (err, createdA, createdB) {
+ should.strictEqual(err, null);
+ BlogPostB.find({author: null}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ });
+ });
+ },
+
+ 'test finding STRICT null matches': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection + random());
+
+ BlogPostB.create(
+ { title: 'A', author: null}
+ , { title: 'B' }, function (err, createdA, createdB) {
+ should.strictEqual(err, null);
+ BlogPostB.find({author: {$in: [null], $exists: true}}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(createdA._id);
+ });
+ });
+ },
+
+ 'setting a path to undefined should retain the value as undefined': function () {
+ var db = start()
+ , B = db.model('BlogPostB', collection + random())
+
+ var doc = new B;
+ doc.title='css3';
+ doc._delta().$set.title.should.equal('css3');
+ doc.title = undefined;
+ doc._delta().$unset.title.should.equal(1);
+ should.strictEqual(undefined, doc._delta().$set);
+
+ doc.title='css3';
+ doc.author = 'aaron';
+ doc.numbers = [3,4,5];
+ doc.meta.date = new Date;
+ doc.meta.visitors = 89;
+ doc.comments = [{ title: 'thanksgiving', body: 'yuuuumm' }];
+ doc.comments.push({ title: 'turkey', body: 'cranberries' });
+
+ doc.save(function (err) {
+ should.strictEqual(null, err);
+ B.findById(doc._id, function (err, b) {
+ should.strictEqual(null, err);
+ b.title.should.equal('css3');
+ b.author.should.equal('aaron');
+ should.equal(b.meta.date.toString(), doc.meta.date.toString());
+ b.meta.visitors.valueOf().should.equal(doc.meta.visitors.valueOf());
+ b.comments.length.should.equal(2);
+ b.comments[0].title.should.equal('thanksgiving');
+ b.comments[0].body.should.equal('yuuuumm');
+ b.comments[1].title.should.equal('turkey');
+ b.comments[1].body.should.equal('cranberries');
+ b.title = undefined;
+ b.author = null;
+ b.meta.date = undefined;
+ b.meta.visitors = null;
+ b.comments[0].title = null;
+ b.comments[0].body = undefined;
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ B.findById(b._id, function (err, b) {
+ should.strictEqual(null, err);
+ should.strictEqual(undefined, b.title);
+ should.strictEqual(null, b.author);
+
+ should.strictEqual(undefined, b.meta.date);
+ should.strictEqual(null, b.meta.visitors);
+ should.strictEqual(null, b.comments[0].title);
+ should.strictEqual(undefined, b.comments[0].body);
+ b.comments[1].title.should.equal('turkey');
+ b.comments[1].body.should.equal('cranberries');
+
+ b.meta = undefined;
+ b.comments = undefined;
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ B.collection.findOne({ _id: b._id}, function (err, b) {
+ db.close();
+ should.strictEqual(null, err);
+ should.strictEqual(undefined, b.meta);
+ should.strictEqual(undefined, b.comments);
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding strings via regular expressions': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: 'Next to Normal'}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.findOne({title: /^Next/}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ var reg = '^Next to Normal$';
+
+ BlogPostB.find({ title: { $regex: reg }}, function (err, found) {
+ should.strictEqual(err, null);
+ found.length.should.equal(1);
+ found[0].id;
+ found[0]._id.should.eql(created._id);
+
+ BlogPostB.findOne({ title: { $regex: reg }}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ BlogPostB.where('title').$regex(reg).findOne(function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ BlogPostB.where('title').$regex(/^Next/).findOne(function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding a document whose arrays contain at least $all values': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create(
+ {numbers: [-1,-2,-3,-4], meta: { visitors: 4 }}
+ , {numbers: [0,-1,-2,-3,-4]}
+ , function (err, whereoutZero, whereZero) {
+ should.strictEqual(err, null);
+
+ BlogPostB.find({numbers: {$all: [-1, -2, -3, -4]}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ BlogPostB.find({'meta.visitors': {$all: [4] }}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(whereoutZero._id);
+ BlogPostB.find({numbers: {$all: [0, -1]}}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(whereZero._id);
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding a document whose arrays contain at least $all string values': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ var post = new BlogPostB({ title: "Aristocats" });
+
+ post.tags.push('onex');
+ post.tags.push('twox');
+ post.tags.push('threex');
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findById(post._id, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPostB.find({ title: { '$all': ['Aristocats']}}, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+
+ BlogPostB.find({ title: { '$all': [/^Aristocats/]}}, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+
+ BlogPostB.find({tags: { '$all': ['onex','twox','threex']}}, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+
+ BlogPostB.find({tags: { '$all': [/^onex/i]}}, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+
+ BlogPostB.findOne({tags: { '$all': /^two/ }}, function (err, doc) {
+ db.close();
+ should.strictEqual(err, null);
+ doc.id.should.eql(post.id);
+ });
+ });
+ });
+ });
+ });
+ });
+
+ });
+ },
+
+ 'find using #all with nested #elemMatch': function () {
+ var db = start()
+ , P = db.model('BlogPostB', collection + '_nestedElemMatch');
+
+ var post = new P({ title: "nested elemMatch" });
+ post.comments.push({ title: 'comment A' }, { title: 'comment B' }, { title: 'comment C' })
+
+ var id0 = post.comments[0]._id;
+ var id1 = post.comments[1]._id;
+ var id2 = post.comments[2]._id;
+
+ post.save(function (err) {
+ should.strictEqual(null, err);
+
+ var query0 = { $elemMatch: { _id: id1, title: 'comment B' }};
+ var query1 = { $elemMatch: { _id: id2.toString(), title: 'comment C' }};
+
+ P.findOne({ comments: { $all: [query0, query1] }}, function (err, p) {
+ db.close();
+ should.strictEqual(null, err);
+ p.id.should.equal(post.id);
+ });
+ });
+ },
+
+ 'find using #or with nested #elemMatch': function () {
+ var db = start()
+ , P = db.model('BlogPostB', collection);
+
+ var post = new P({ title: "nested elemMatch" });
+ post.comments.push({ title: 'comment D' }, { title: 'comment E' }, { title: 'comment F' })
+
+ var id0 = post.comments[0]._id;
+ var id1 = post.comments[1]._id;
+ var id2 = post.comments[2]._id;
+
+ post.save(function (err) {
+ should.strictEqual(null, err);
+
+ var query0 = { comments: { $elemMatch: { title: 'comment Z' }}};
+ var query1 = { comments: { $elemMatch: { _id: id1.toString(), title: 'comment E' }}};
+
+ P.findOne({ $or: [query0, query1] }, function (err, p) {
+ db.close();
+ should.strictEqual(null, err);
+ p.id.should.equal(post.id);
+ });
+ });
+ },
+
+ 'find using #nor with nested #elemMatch': function () {
+ var db = start()
+ , P = db.model('BlogPostB', collection + '_norWithNestedElemMatch');
+
+ var p0 = { title: "nested $nor elemMatch1", comments: [] };
+
+ var p1 = { title: "nested $nor elemMatch0", comments: [] };
+ p1.comments.push({ title: 'comment X' }, { title: 'comment Y' }, { title: 'comment W' })
+
+ P.create(p0, p1, function (err, post0, post1) {
+ should.strictEqual(null, err);
+
+ var id = post1.comments[1]._id;
+
+ var query0 = { comments: { $elemMatch: { title: 'comment Z' }}};
+ var query1 = { comments: { $elemMatch: { _id: id.toString(), title: 'comment Y' }}};
+
+ P.find({ $nor: [query0, query1] }, function (err, posts) {
+ db.close();
+ should.strictEqual(null, err);
+ posts.length.should.equal(1);
+ posts[0].id.should.equal(post0.id);
+ });
+ });
+
+ },
+
+ 'test finding documents where an array of a certain $size': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({numbers: [1,2,3,4,5,6,7,8,9,10]}, function (err, whereoutZero) {
+ should.strictEqual(err, null);
+ BlogPostB.create({numbers: [11,12,13,14,15,16,17,18,19,20]}, function (err, whereZero) {
+ should.strictEqual(err, null);
+ BlogPostB.create({numbers: [1,2,3,4,5,6,7,8,9,10,11]}, function (err, found) {
+ BlogPostB.find({numbers: {$size: 10}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ BlogPostB.find({numbers: {$size: 11}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ db.close();
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding documents where an array where the $slice operator': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({numbers: [500,600,700,800]}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.findById(created._id, {numbers: {$slice: 2}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ found.numbers.should.have.length(2);
+ found.numbers[0].should.equal(500);
+ found.numbers[1].should.equal(600);
+ BlogPostB.findById(created._id, {numbers: {$slice: -2}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ found.numbers.should.have.length(2);
+ found.numbers[0].should.equal(700);
+ found.numbers[1].should.equal(800);
+ BlogPostB.findById(created._id, {numbers: {$slice: [1, 2]}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ found.numbers.should.have.length(2);
+ found.numbers[0].should.equal(600);
+ found.numbers[1].should.equal(700);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding documents with a specifc Buffer in their array': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({sigs: [new Buffer([1, 2, 3]),
+ new Buffer([4, 5, 6]),
+ new Buffer([7, 8, 9])]}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.findOne({sigs: new Buffer([1, 2, 3])}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ var query = { sigs: { "$in" : [new Buffer([3, 3, 3]), new Buffer([4, 5, 6])] } };
+ BlogPostB.findOne(query, function (err, found) {
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test limits': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: 'first limit'}, function (err, first) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: 'second limit'}, function (err, second) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: 'third limit'}, function (err, third) {
+ should.strictEqual(err, null);
+ BlogPostB.find({title: /limit$/}).limit(2).find( function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ found[0]._id.should.eql(first._id);
+ found[1]._id.should.eql(second._id);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test skips': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: 'first skip'}, function (err, first) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: 'second skip'}, function (err, second) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: 'third skip'}, function (err, third) {
+ should.strictEqual(err, null);
+ BlogPostB.find({title: /skip$/}).skip(1).limit(2).find( function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ found[0]._id.should.eql(second._id);
+ found[1]._id.should.eql(third._id);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test sorts': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({meta: {visitors: 100}}, function (err, least) {
+ should.strictEqual(err, null);
+ BlogPostB.create({meta: {visitors: 300}}, function (err, largest) {
+ should.strictEqual(err, null);
+ BlogPostB.create({meta: {visitors: 200}}, function (err, middle) {
+ should.strictEqual(err, null);
+ BlogPostB
+ .where('meta.visitors').gt(99).lt(301)
+ .sort('meta.visitors', -1)
+ .find( function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(3);
+ found[0]._id.should.eql(largest._id);
+ found[1]._id.should.eql(middle._id);
+ found[2]._id.should.eql(least._id);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test backwards compatibility with previously existing null values in db': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , post = new BlogPostB();
+
+ post.collection.insert({ meta: { visitors: 9898, a: null } }, {}, function (err, b) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({_id: b[0]._id}, function (err, found) {
+ should.strictEqual(err, null);
+ found.get('meta.visitors').valueOf().should.eql(9898);
+ db.close();
+ })
+ })
+ },
+
+ 'test backwards compatibility with unused values in db': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , post = new BlogPostB();
+
+ post.collection.insert({ meta: { visitors: 9898, color: 'blue'}}, {}, function (err, b) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({_id: b[0]._id}, function (err, found) {
+ should.strictEqual(err, null);
+ found.get('meta.visitors').valueOf().should.eql(9898);
+ found.save(function (err) {
+ should.strictEqual(err, null);
+ db.close();
+ })
+ })
+ })
+ },
+
+ 'test streaming cursors with #each': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: "The Wrestler", tags: ["movie"]}, function (err, wrestler) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: "Black Swan", tags: ["movie"]}, function (err, blackswan) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: "Pi", tags: ["movie"]}, function (err, pi) {
+ should.strictEqual(err, null);
+ var found = {};
+ BlogPostB
+ .find({tags: "movie"})
+ .sort('title', -1)
+ .each(function (err, post) {
+ should.strictEqual(err, null);
+ if (post) found[post.title] = 1;
+ else {
+ found.should.have.property("The Wrestler", 1);
+ found.should.have.property("Black Swan", 1);
+ found.should.have.property("Pi", 1);
+ db.close();
+ }
+ });
+ });
+ });
+ });
+ },
+
+ 'test streaming cursors with #each and manual iteration': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: "Bleu", tags: ["Krzysztof Kieślowski"]}, function (err, wrestler) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: "Blanc", tags: ["Krzysztof Kieślowski"]}, function (err, blackswan) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: "Rouge", tags: ["Krzysztof Kieślowski"]}, function (err, pi) {
+ should.strictEqual(err, null);
+ var found = {};
+ BlogPostB
+ .find({tags: "Krzysztof Kieślowski"})
+ .each(function (err, post, next) {
+ should.strictEqual(err, null);
+ if (post) {
+ found[post.title] = 1;
+ process.nextTick(next);
+ } else {
+ found.should.have.property("Bleu", 1);
+ found.should.have.property("Blanc", 1);
+ found.should.have.property("Rouge", 1);
+ db.close();
+ }
+ });
+ });
+ });
+ });
+ },
+
+ '$gt, $lt, $lte, $gte work on strings': function () {
+ var db = start()
+ var D = db.model('D', new Schema({dt: String}), collection);
+
+ D.create({ dt: '2011-03-30' }, done);
+ D.create({ dt: '2011-03-31' }, done);
+ D.create({ dt: '2011-04-01' }, done);
+ D.create({ dt: '2011-04-02' }, done);
+
+ var pending = 3;
+ function done (err) {
+ if (err) db.close();
+ should.strictEqual(err, null);
+
+ if (--pending) return;
+
+ pending = 2;
+
+ D.find({ 'dt': { $gte: '2011-03-30', $lte: '2011-04-01' }}).sort('dt', 1).run(function (err, docs) {
+ if (--pending) db.close();
+ should.strictEqual(err, null);
+ docs.length.should.eql(3);
+ docs[0].dt.should.eql('2011-03-30');
+ docs[1].dt.should.eql('2011-03-31');
+ docs[2].dt.should.eql('2011-04-01');
+ docs.some(function (d) { return '2011-04-02' === d.dt }).should.be.false;
+ });
+
+ D.find({ 'dt': { $gt: '2011-03-30', $lt: '2011-04-02' }}).sort('dt', 1).run(function (err, docs) {
+ if (--pending) db.close();
+ should.strictEqual(err, null);
+ docs.length.should.eql(2);
+ docs[0].dt.should.eql('2011-03-31');
+ docs[1].dt.should.eql('2011-04-01');
+ docs.some(function (d) { return '2011-03-30' === d.dt }).should.be.false;
+ docs.some(function (d) { return '2011-04-02' === d.dt }).should.be.false;
+ });
+ }
+ },
+
+ 'nested mixed queries (x.y.z)': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.find({ 'mixed.nested.stuff': 'skynet' }, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ },
+
+ // GH-336
+ 'finding by Date field works': function () {
+ var db = start()
+ , Test = db.model('TestDateQuery', new Schema({ date: Date }), 'datetest_' + random())
+ , now = new Date;
+
+ Test.create({ date: now }, { date: new Date(now-10000) }, function (err, a, b) {
+ should.strictEqual(err, null);
+ Test.find({ date: now }, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+ });
+ });
+ },
+
+ // GH-309
+ 'using $near with Arrays works (geo-spatial)': function () {
+ var db = start()
+ , Test = db.model('Geo1', geoSchema, 'geospatial'+random());
+
+ Test.create({ loc: [ 10, 20 ]}, { loc: [ 40, 90 ]}, function (err) {
+ should.strictEqual(err, null);
+ setTimeout(function () {
+ Test.find({ loc: { $near: [30, 40] }}, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ docs.length.should.equal(2);
+ });
+ }, 700);
+ });
+ },
+
+ // GH-586
+ 'using $within with Arrays works (geo-spatial)': function () {
+ var db = start()
+ , Test = db.model('Geo2', geoSchema, collection + 'geospatial');
+
+ Test.create({ loc: [ 35, 50 ]}, { loc: [ -40, -90 ]}, function (err) {
+ should.strictEqual(err, null);
+ setTimeout(function () {
+ Test.find({ loc: { '$within': { '$box': [[30,40], [40,60]] }}}, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+ });
+ }, 700);
+ });
+ },
+
+ // GH-610
+ 'using nearSphere with Arrays works (geo-spatial)': function () {
+ var db = start()
+ , Test = db.model('Geo3', geoSchema, "y"+random());
+
+ Test.create({ loc: [ 10, 20 ]}, { loc: [ 40, 90 ]}, function (err) {
+ should.strictEqual(err, null);
+ setTimeout(function () {
+ Test.find({ loc: { $nearSphere: [30, 40] }}, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ docs.length.should.equal(2);
+ });
+ }, 700);
+ });
+ },
+
+ 'using $maxDistance with Array works (geo-spatial)': function () {
+ var db = start()
+ , Test = db.model('Geo4', geoSchema, "x"+random());
+
+ Test.create({ loc: [ 20, 80 ]}, { loc: [ 25, 30 ]}, function (err, docs) {
+ should.strictEqual(!!err, false);
+ setTimeout(function () {
+ Test.find({ loc: { $near: [25, 31], $maxDistance: 1 }}, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+ Test.find({ loc: { $near: [25, 32], $maxDistance: 1 }}, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ docs.length.should.equal(0);
+ });
+ });
+ }, 500);
+ });
+ },
+
+ '$type tests': function () {
+ var db = start()
+ , B = db.model('BlogPostB', collection);
+
+ B.find({ title: { $type: "asd" }}, function (err, posts) {
+ err.message.should.eql("$type parameter must be Number");
+
+ B.find({ title: { $type: 2 }}, function (err, posts) {
+ db.close();
+ should.strictEqual(null, err);
+ should.strictEqual(Array.isArray(posts), true);
+ });
+ });
+ },
+
+ 'buffers find using available types': function () {
+ var db = start()
+ , BufSchema = new Schema({ name: String, block: Buffer })
+ , Test = db.model('Buffer', BufSchema, "buffers");
+
+ var docA = { name: 'A', block: new Buffer('über') };
+ var docB = { name: 'B', block: new Buffer("buffer shtuffs are neat") };
+ var docC = { name: 'C', block: 'hello world' };
+
+ Test.create(docA, docB, docC, function (err, a, b, c) {
+ should.strictEqual(err, null);
+ b.block.toString('utf8').should.equal('buffer shtuffs are neat');
+ a.block.toString('utf8').should.equal('über');
+ c.block.toString('utf8').should.equal('hello world');
+
+ Test.findById(a._id, function (err, a) {
+ should.strictEqual(err, null);
+ a.block.toString('utf8').should.equal('über');
+
+ Test.findOne({ block: 'buffer shtuffs are neat' }, function (err, rb) {
+ should.strictEqual(err, null);
+ rb.block.toString('utf8').should.equal('buffer shtuffs are neat');
+
+ Test.findOne({ block: /buffer/i }, function (err, rb) {
+ err.message.should.eql('Cast to buffer failed for value "/buffer/i"')
+ Test.findOne({ block: [195, 188, 98, 101, 114] }, function (err, rb) {
+ should.strictEqual(err, null);
+ rb.block.toString('utf8').should.equal('über');
+
+ Test.findOne({ block: 'aGVsbG8gd29ybGQ=' }, function (err, rb) {
+ should.strictEqual(err, null);
+ should.strictEqual(rb, null);
+
+ Test.findOne({ block: new Buffer('aGVsbG8gd29ybGQ=', 'base64') }, function (err, rb) {
+ should.strictEqual(err, null);
+ rb.block.toString('utf8').should.equal('hello world');
+
+ Test.findOne({ block: new MongooseBuffer('aGVsbG8gd29ybGQ=', 'base64') }, function (err, rb) {
+ should.strictEqual(err, null);
+ rb.block.toString('utf8').should.equal('hello world');
+
+ Test.remove({}, function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+
+ });
+ },
+
+ 'buffer tests using conditionals': function () {
+ // $in $nin etc
+ var db = start()
+ , BufSchema = new Schema({ name: String, block: Buffer })
+ , Test = db.model('Buffer2', BufSchema, "buffer_"+random());
+
+ var docA = { name: 'A', block: new MongooseBuffer([195, 188, 98, 101, 114]) }; //über
+ var docB = { name: 'B', block: new MongooseBuffer("buffer shtuffs are neat") };
+ var docC = { name: 'C', block: new MongooseBuffer('aGVsbG8gd29ybGQ=', 'base64') };
+
+ Test.create(docA, docB, docC, function (err, a, b, c) {
+ should.strictEqual(err, null);
+ a.block.toString('utf8').should.equal('über');
+ b.block.toString('utf8').should.equal('buffer shtuffs are neat');
+ c.block.toString('utf8').should.equal('hello world');
+
+ Test.find({ block: { $in: [[195, 188, 98, 101, 114], "buffer shtuffs are neat", new Buffer('aGVsbG8gd29ybGQ=', 'base64')] }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(3);
+ });
+
+ Test.find({ block: { $in: ['über', 'hello world'] }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(2);
+ });
+
+ Test.find({ block: { $in: ['über'] }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(1);
+ tests[0].block.toString('utf8').should.equal('über');
+ });
+
+ Test.find({ block: { $nin: ['über'] }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(2);
+ });
+
+ Test.find({ block: { $nin: [[195, 188, 98, 101, 114], new Buffer('aGVsbG8gd29ybGQ=', 'base64')] }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(1);
+ tests[0].block.toString('utf8').should.equal('buffer shtuffs are neat');
+ });
+
+ Test.find({ block: { $ne: 'über' }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(2);
+ });
+
+ Test.find({ block: { $gt: 'über' }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(2);
+ });
+
+ Test.find({ block: { $gte: 'über' }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(3);
+ });
+
+ Test.find({ block: { $lt: new Buffer('buffer shtuffs are neat') }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(2);
+ tests[0].block.toString('utf8').should.equal('über');
+ });
+
+ Test.find({ block: { $lte: 'buffer shtuffs are neat' }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(3);
+ });
+
+ var pending = 9;
+ function done () {
+ if (--pending) return;
+ Test.remove({}, function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ }
+ });
+ },
+
+ // gh-591
+ 'querying Mixed types with elemMatch': function () {
+ var db = start()
+ , S = new Schema({ a: [{}], b: Number })
+ , M = db.model('QueryingMixedArrays', S, random())
+
+ var m = new M;
+ m.a = [1,2,{ name: 'Frodo' },'IDK', {name: 100}];
+ m.b = 10;
+
+ m.save(function (err) {
+ should.strictEqual(null, err);
+
+ M.find({ a: { name: 'Frodo' }, b: '10' }, function (err, docs) {
+ should.strictEqual(null, err);
+ docs[0].a.length.should.equal(5);
+ docs[0].b.valueOf().should.equal(10);
+
+ var query = {
+ a: {
+ $elemMatch: { name: 100 }
+ }
+ }
+
+ M.find(query, function (err, docs) {
+ db.close();
+ should.strictEqual(null, err);
+ docs[0].a.length.should.equal(5);
+ });
+ });
+ });
+ },
+
+ // gh-599
+ 'regex with Array should work': function () {
+ var db = start()
+ , B = db.model('BlogPostB', random())
+
+ B.create({ tags: 'wooof baaaark meeeeow'.split(' ') }, function (err, b) {
+ should.strictEqual(null, err);
+ B.findOne({ tags: /ooof$/ }, function (err, doc) {
+ should.strictEqual(null, err);
+ should.strictEqual(true, !! doc);
+ ;(!! ~doc.tags.indexOf('meeeeow')).should.be.true;
+
+ B.findOne({ tags: {$regex: 'eow$' } }, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ should.strictEqual(true, !! doc);
+ ;(!! ~doc.tags.indexOf('meeeeow')).should.be.true;
+ });
+ });
+ });
+ },
+
+ // gh-640
+ 'updating a number to null': function () {
+ var db = start()
+ var B = db.model('BlogPostB')
+ var b = new B({ meta: { visitors: null }});
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ B.findById(b, function (err, b) {
+ should.strictEqual(null, err);
+ should.strictEqual(b.meta.visitors, null);
+
+ B.update({ _id: b._id }, { meta: { visitors: null }}, function (err, docs) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ },
+
+ // gh-690
+ 'using $all with ObjectIds': function () {
+ var db = start()
+
+ var SSchema = new Schema({ name: String });
+ var PSchema = new Schema({ sub: [SSchema] });
+
+ var P = db.model('usingAllWithObjectIds', PSchema);
+ var sub = [{ name: 'one' }, { name: 'two' }, { name: 'three' }];
+
+ P.create({ sub: sub }, function (err, p) {
+ should.strictEqual(null, err);
+
+ var o0 = p.sub[0]._id;
+ var o1 = p.sub[1]._id;
+ var o2 = p.sub[2]._id;
+
+ P.findOne({ 'sub._id': { $all: [o1, o2] }}, function (err, doc) {
+ should.strictEqual(null, err);
+ doc.id.should.equal(p.id);
+
+ P.findOne({ 'sub._id': { $all: [o0, new DocumentObjectId] }}, function (err, doc) {
+ should.strictEqual(null, err);
+ should.equal(false, !!doc);
+
+ P.findOne({ 'sub._id': { $all: [o2] }}, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.id.should.equal(p.id);
+ });
+ });
+ });
+ });
+ },
+
+ 'using $all with Dates': function () {
+ var db = start()
+
+ var SSchema = new Schema({ d: Date });
+ var PSchema = new Schema({ sub: [SSchema] });
+
+ var P = db.model('usingAllWithDates', PSchema);
+ var sub = [
+ { d: new Date }
+ , { d: new Date(Date.now()-10000) }
+ , { d: new Date(Date.now()-30000) }
+ ];
+
+ P.create({ sub: sub }, function (err, p) {
+ should.strictEqual(null, err);
+
+ var o0 = p.sub[0].d;
+ var o1 = p.sub[1].d;
+ var o2 = p.sub[2].d;
+
+ P.findOne({ 'sub.d': { $all: [o1, o2] }}, function (err, doc) {
+ should.strictEqual(null, err);
+ doc.id.should.equal(p.id);
+
+ P.findOne({ 'sub.d': { $all: [o0, new Date] }}, function (err, doc) {
+ should.strictEqual(null, err);
+ should.equal(false, !!doc);
+
+ P.findOne({ 'sub.d': { $all: [o2] }}, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.id.should.equal(p.id);
+ });
+ });
+ });
+ });
+ },
+
+ 'excluding paths by schematype': function () {
+ var db =start()
+
+ var schema = new Schema({
+ thin: Boolean
+ , name: { type: String, select: false }
+ });
+
+ var S = db.model('ExcludingBySchemaType', schema);
+ S.create({ thin: true, name: 'the excluded' },function (err, s) {
+ should.strictEqual(null, err);
+ s.name.should.equal('the excluded');
+ S.findById(s, function (err, s) {
+ db.close();
+ should.strictEqual(null, err);
+ s.isSelected('name').should.be.false;
+ should.strictEqual(undefined, s.name);
+ });
+ });
+ },
+
+ 'excluding paths through schematype': function () {
+ var db =start()
+
+ var schema = new Schema({
+ thin: Boolean
+ , name: { type: String, select: false}
+ });
+
+ var S = db.model('ExcludingBySchemaType', schema);
+ S.create({ thin: true, name: 'the excluded' },function (err, s) {
+ should.strictEqual(null, err);
+ s.name.should.equal('the excluded');
+
+ var pending = 2;
+ function done (err, s) {
+ --pending || db.close();
+ if (Array.isArray(s)) s = s[0];
+ should.strictEqual(null, err);
+ s.isSelected('name').should.be.false;
+ should.strictEqual(undefined, s.name);
+ }
+
+ S.findById(s).exclude('thin').exec(done);
+ S.find({ _id: s._id }).select('thin').exec(done);
+ });
+ },
+
+ 'including paths through schematype': function () {
+ var db =start()
+
+ var schema = new Schema({
+ thin: Boolean
+ , name: { type: String, select: true }
+ });
+
+ var S = db.model('IncludingBySchemaType', schema);
+ S.create({ thin: true, name: 'the included' },function (err, s) {
+ should.strictEqual(null, err);
+ s.name.should.equal('the included');
+
+ var pending = 2;
+ function done (err, s) {
+ --pending || db.close();
+ if (Array.isArray(s)) s = s[0];
+ should.strictEqual(null, err);
+ s.isSelected('name').should.be.true;
+ s.name.should.equal('the included');
+ }
+
+ S.findById(s).exclude('thin').exec(done);
+ S.find({ _id: s._id }).select('thin').exec(done);
+ });
+ },
+
+ 'overriding schematype select options': function () {
+ var db =start()
+
+ var selected = new Schema({
+ thin: Boolean
+ , name: { type: String, select: true }
+ });
+ var excluded = new Schema({
+ thin: Boolean
+ , name: { type: String, select: false }
+ });
+
+ var S = db.model('OverriddingSelectedBySchemaType', selected);
+ var E = db.model('OverriddingExcludedBySchemaType', excluded);
+
+ var pending = 4;
+
+ S.create({ thin: true, name: 'the included' },function (err, s) {
+ should.strictEqual(null, err);
+ s.name.should.equal('the included');
+
+ S.find({ _id: s._id }).select('thin name').exec(function (err, s) {
+ --pending || db.close();
+ s = s[0];
+ should.strictEqual(null, err);
+ s.isSelected('name').should.be.true;
+ s.isSelected('thin').should.be.true;
+ s.name.should.equal('the included');
+ s.thin.should.be.true;
+ });
+
+ S.findById(s).exclude('name').exec(function (err, s) {
+ --pending || db.close();
+ should.strictEqual(null, err);
+ s.isSelected('name').should.be.false;
+ s.isSelected('thin').should.be.true;
+ should.equal(undefined, s.name);
+ should.equal(true, s.thin);
+ })
+ });
+
+ E.create({ thin: true, name: 'the excluded' },function (err, e) {
+ should.strictEqual(null, err);
+ e.name.should.equal('the excluded');
+
+ E.find({ _id: e._id }).select('thin name').exec(function (err, e) {
+ --pending || db.close();
+ e = e[0];
+ should.strictEqual(null, err);
+ e.isSelected('name').should.be.true;
+ e.isSelected('thin').should.be.true;
+ e.name.should.equal('the excluded');
+ e.thin.should.be.true;
+ });
+
+ E.findById(e).exclude('name').exec(function (err, e) {
+ --pending || db.close();
+ should.strictEqual(null, err);
+ e.isSelected('name').should.be.false;
+ e.isSelected('thin').should.be.true;
+ should.equal(undefined, e.name);
+ should.equal(true, e.thin);
+ })
+ });
+ },
+
+ 'conflicting schematype path selection should error': function () {
+ var db =start()
+
+ var schema = new Schema({
+ thin: Boolean
+ , name: { type: String, select: true }
+ , conflict: { type: String, select: false}
+ });
+
+ var S = db.model('ConflictingBySchemaType', schema);
+ S.create({ thin: true, name: 'bing', conflict: 'crosby' },function (err, s) {
+ should.strictEqual(null, err);
+ s.name.should.equal('bing');
+ s.conflict.should.equal('crosby');
+
+ var pending = 2;
+ function done (err, s) {
+ --pending || db.close();
+ if (Array.isArray(s)) s = s[0];
+ should.equal(true, !!err);
+ }
+
+ S.findById(s).exec(done);
+ S.find({ _id: s._id }).exec(done);
+ });
+ },
+};
diff --git a/node_modules/mongoose/test/model.ref.test.js b/node_modules/mongoose/test/model.ref.test.js
new file mode 100644
index 0000000..967b6e4
--- /dev/null
+++ b/node_modules/mongoose/test/model.ref.test.js
@@ -0,0 +1,1528 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Schema = mongoose.Schema
+ , ObjectId = Schema.ObjectId
+ , DocObjectId = mongoose.Types.ObjectId
+
+/**
+ * Setup.
+ */
+
+/**
+ * User schema.
+ */
+
+var User = new Schema({
+ name : String
+ , email : String
+ , gender : { type: String, enum: ['male', 'female'], default: 'male' }
+ , age : { type: Number, default: 21 }
+ , blogposts : [{ type: ObjectId, ref: 'RefBlogPost' }]
+});
+
+/**
+ * Comment subdocument schema.
+ */
+
+var Comment = new Schema({
+ _creator : { type: ObjectId, ref: 'RefUser' }
+ , content : String
+});
+
+/**
+ * Blog post schema.
+ */
+
+var BlogPost = new Schema({
+ _creator : { type: ObjectId, ref: 'RefUser' }
+ , title : String
+ , comments : [Comment]
+ , fans : [{ type: ObjectId, ref: 'RefUser' }]
+});
+
+var posts = 'blogposts_' + random()
+ , users = 'users_' + random();
+
+mongoose.model('RefBlogPost', BlogPost);
+mongoose.model('RefUser', User);
+
+/**
+ * Tests.
+ */
+
+module.exports = {
+
+ 'test populating a single reference': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users)
+
+ User.create({
+ name : 'Guillermo'
+ , email : 'rauchg@gmail.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post._creator.should.be.an.instanceof(User);
+ post._creator.name.should.equal('Guillermo');
+ post._creator.email.should.equal('rauchg@gmail.com');
+ });
+ });
+ });
+ },
+
+ 'test an error in single reference population propagates': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts + '1')
+ , User = db.model('RefUser', users + '1');
+
+ User.create({
+ name: 'Guillermo'
+ , email: 'rauchg@gmail.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ var origFind = User.findOne;
+
+ // mock an error
+ User.findOne = function () {
+ var args = Array.prototype.map.call(arguments, function (arg) {
+ return 'function' == typeof arg ? function () {
+ arg(new Error('woot'));
+ } : arg;
+ });
+ return origFind.apply(this, args);
+ };
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .run(function (err, post) {
+ db.close();
+ err.should.be.an.instanceof(Error);
+ err.message.should.equal('woot');
+ });
+ });
+ });
+ },
+
+ 'test populating with partial fields selection': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Guillermo'
+ , email : 'rauchg@gmail.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator', ['email'])
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post._creator.should.be.an.instanceof(User);
+ post._creator.isInit('name').should.be.false;
+ post._creator.email.should.equal('rauchg@gmail.com');
+ });
+ });
+ });
+ },
+
+ 'populating single oid with partial field selection and filter': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Banana'
+ , email : 'cats@example.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator', 'email', { name: 'Peanut' })
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+ should.strictEqual(post._creator, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator', 'email', { name: 'Banana' })
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+ post._creator.should.be.an.instanceof(User);
+ post._creator.isInit('name').should.be.false;
+ post._creator.email.should.equal('cats@example.com');
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating and changing a reference': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Guillermo'
+ , email : 'rauchg@gmail.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+
+ post._creator.should.be.an.instanceof(User);
+ post._creator.name.should.equal('Guillermo');
+ post._creator.email.should.equal('rauchg@gmail.com');
+
+ User.create({
+ name : 'Aaron'
+ , email : 'aaron@learnboost.com'
+ }, function (err, newCreator) {
+ should.strictEqual(err, null);
+
+ post._creator = newCreator._id;
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post._creator.name.should.equal('Aaron');
+ post._creator.email.should.equal('aaron@learnboost.com');
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating with partial fields selection and changing ref': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Guillermo'
+ , email : 'rauchg@gmail.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator', {'name': 1})
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+
+ post._creator.should.be.an.instanceof(User);
+ post._creator.name.should.equal('Guillermo');
+
+ User.create({
+ name : 'Aaron'
+ , email : 'aaron@learnboost.com'
+ }, function (err, newCreator) {
+ should.strictEqual(err, null);
+
+ post._creator = newCreator._id;
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator', {email: 0})
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post._creator.name.should.equal('Aaron');
+ should.not.exist(post._creator.email);
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of references and fetching many': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans')
+ .run(function (err, blogposts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ blogposts[0].fans[0].name.should.equal('Fan 1');
+ blogposts[0].fans[0].email.should.equal('fan1@learnboost.com');
+ blogposts[0].fans[1].name.should.equal('Fan 2');
+ blogposts[0].fans[1].email.should.equal('fan2@learnboost.com');
+
+ blogposts[1].fans[0].name.should.equal('Fan 2');
+ blogposts[1].fans[0].email.should.equal('fan2@learnboost.com');
+ blogposts[1].fans[1].name.should.equal('Fan 1');
+ blogposts[1].fans[1].email.should.equal('fan1@learnboost.com');
+
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test an error in array reference population propagates': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts + '2')
+ , User = db.model('RefUser', users + '2');
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ // mock an error
+ var origFind = User.find;
+ User.find = function () {
+ var args = Array.prototype.map.call(arguments, function (arg) {
+ return 'function' == typeof arg ? function () {
+ arg(new Error('woot 2'));
+ } : arg;
+ });
+ return origFind.apply(this, args);
+ };
+
+ BlogPost
+ .find({ $or: [{ _id: post1._id }, { _id: post2._id }] })
+ .populate('fans')
+ .run(function (err, blogposts) {
+ db.close();
+ err.should.be.an.instanceof(Error);
+ err.message.should.equal('woot 2');
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of references with fields selection': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', 'name')
+ .run(function (err, blogposts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ blogposts[0].fans[0].name.should.equal('Fan 1');
+ blogposts[0].fans[0].isInit('email').should.be.false;
+ blogposts[0].fans[1].name.should.equal('Fan 2');
+ blogposts[0].fans[1].isInit('email').should.be.false;
+ should.strictEqual(blogposts[0].fans[1].email, undefined);
+
+ blogposts[1].fans[0].name.should.equal('Fan 2');
+ blogposts[1].fans[0].isInit('email').should.be.false;
+ blogposts[1].fans[1].name.should.equal('Fan 1');
+ blogposts[1].fans[1].isInit('email').should.be.false;
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of references and filtering': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ , gender : 'female'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 3'
+ , email : 'fan3@learnboost.com'
+ , gender : 'female'
+ }, function (err, fan3) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2, fan3]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan3, fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', '', { gender: 'female', _id: { $in: [fan2] }})
+ .run(function (err, blogposts) {
+ should.strictEqual(err, null);
+
+ blogposts[0].fans.length.should.equal(1);
+ blogposts[0].fans[0].gender.should.equal('female');
+ blogposts[0].fans[0].name.should.equal('Fan 2');
+ blogposts[0].fans[0].email.should.equal('fan2@learnboost.com');
+
+ blogposts[1].fans.length.should.equal(1);
+ blogposts[1].fans[0].gender.should.equal('female');
+ blogposts[1].fans[0].name.should.equal('Fan 2');
+ blogposts[1].fans[0].email.should.equal('fan2@learnboost.com');
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', false, { gender: 'female' })
+ .run(function (err, blogposts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ should.strictEqual(blogposts[0].fans.length, 2);
+ blogposts[0].fans[0].gender.should.equal('female');
+ blogposts[0].fans[0].name.should.equal('Fan 2');
+ blogposts[0].fans[0].email.should.equal('fan2@learnboost.com');
+ blogposts[0].fans[1].gender.should.equal('female');
+ blogposts[0].fans[1].name.should.equal('Fan 3');
+ blogposts[0].fans[1].email.should.equal('fan3@learnboost.com');
+
+ should.strictEqual(blogposts[1].fans.length, 2);
+ blogposts[1].fans[0].gender.should.equal('female');
+ blogposts[1].fans[0].name.should.equal('Fan 3');
+ blogposts[1].fans[0].email.should.equal('fan3@learnboost.com');
+ blogposts[1].fans[1].gender.should.equal('female');
+ blogposts[1].fans[1].name.should.equal('Fan 2');
+ blogposts[1].fans[1].email.should.equal('fan2@learnboost.com');
+ });
+
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of references and multi-filtering': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ , gender : 'female'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 3'
+ , email : 'fan3@learnboost.com'
+ , gender : 'female'
+ , age : 25
+ }, function (err, fan3) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2, fan3]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan3, fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', undefined, { _id: fan3 })
+ .run(function (err, blogposts) {
+ should.strictEqual(err, null);
+
+ blogposts[0].fans.length.should.equal(1);
+ blogposts[0].fans[0].gender.should.equal('female');
+ blogposts[0].fans[0].name.should.equal('Fan 3');
+ blogposts[0].fans[0].email.should.equal('fan3@learnboost.com');
+ should.equal(blogposts[0].fans[0].age, 25);
+
+ blogposts[1].fans.length.should.equal(1);
+ blogposts[1].fans[0].gender.should.equal('female');
+ blogposts[1].fans[0].name.should.equal('Fan 3');
+ blogposts[1].fans[0].email.should.equal('fan3@learnboost.com');
+ should.equal(blogposts[1].fans[0].age, 25);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', 0, { gender: 'female' })
+ .run(function (err, blogposts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ blogposts[0].fans.length.should.equal(2);
+ blogposts[0].fans[0].gender.should.equal('female');
+ blogposts[0].fans[0].name.should.equal('Fan 2');
+ blogposts[0].fans[0].email.should.equal('fan2@learnboost.com');
+ blogposts[0].fans[1].gender.should.equal('female');
+ blogposts[0].fans[1].name.should.equal('Fan 3');
+ blogposts[0].fans[1].email.should.equal('fan3@learnboost.com');
+ should.equal(blogposts[0].fans[1].age, 25);
+
+ blogposts[1].fans.length.should.equal(2);
+ blogposts[1].fans[0].gender.should.equal('female');
+ blogposts[1].fans[0].name.should.equal('Fan 3');
+ blogposts[1].fans[0].email.should.equal('fan3@learnboost.com');
+ should.equal(blogposts[1].fans[0].age, 25);
+ blogposts[1].fans[1].gender.should.equal('female');
+ blogposts[1].fans[1].name.should.equal('Fan 2');
+ blogposts[1].fans[1].email.should.equal('fan2@learnboost.com');
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of references and multi-filtering with field selection': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ , gender : 'female'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 3'
+ , email : 'fan3@learnboost.com'
+ , gender : 'female'
+ , age : 25
+ }, function (err, fan3) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2, fan3]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan3, fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', 'name email', { gender: 'female', age: 25 })
+ .run(function (err, blogposts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ should.strictEqual(blogposts[0].fans.length, 1);
+ blogposts[0].fans[0].name.should.equal('Fan 3');
+ blogposts[0].fans[0].email.should.equal('fan3@learnboost.com');
+ blogposts[0].fans[0].isInit('email').should.be.true;
+ blogposts[0].fans[0].isInit('gender').should.be.false;
+ blogposts[0].fans[0].isInit('age').should.be.false;
+
+ should.strictEqual(blogposts[1].fans.length, 1);
+ blogposts[1].fans[0].name.should.equal('Fan 3');
+ blogposts[1].fans[0].email.should.equal('fan3@learnboost.com');
+ blogposts[1].fans[0].isInit('email').should.be.true;
+ blogposts[1].fans[0].isInit('gender').should.be.false;
+ blogposts[1].fans[0].isInit('age').should.be.false;
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of refs, changing one, and removing one': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, {
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ }, {
+ name : 'Fan 3'
+ , email : 'fan3@learnboost.com'
+ }, {
+ name : 'Fan 4'
+ , email : 'fan4@learnboost.com'
+ }, function (err, fan1, fan2, fan3, fan4) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2]
+ }, {
+ title : 'Woot'
+ , fans : [fan2, fan1]
+ }, function (err, post1, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', ['name'])
+ .run(function (err, blogposts) {
+ should.strictEqual(err, null);
+
+ blogposts[0].fans[0].name.should.equal('Fan 1');
+ blogposts[0].fans[0].isInit('email').should.be.false;
+ blogposts[0].fans[1].name.should.equal('Fan 2');
+ blogposts[0].fans[1].isInit('email').should.be.false;
+
+ blogposts[1].fans[0].name.should.equal('Fan 2');
+ blogposts[1].fans[0].isInit('email').should.be.false;
+ blogposts[1].fans[1].name.should.equal('Fan 1');
+ blogposts[1].fans[1].isInit('email').should.be.false;
+
+ blogposts[1].fans = [fan3, fan4];
+
+ blogposts[1].save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(blogposts[1]._id, [], { populate: ['fans'] })
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+
+ post.fans[0].name.should.equal('Fan 3');
+ post.fans[1].name.should.equal('Fan 4');
+
+ post.fans.splice(0, 1);
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('fans')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+ post.fans.length.should.equal(1);
+ post.fans[0].name.should.equal('Fan 4');
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating subdocuments': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({ name: 'User 1' }, function (err, user1) {
+ should.strictEqual(err, null);
+
+ User.create({ name: 'User 2' }, function (err, user2) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title: 'Woot'
+ , _creator: user1._id
+ , comments: [
+ { _creator: user1._id, content: 'Woot woot' }
+ , { _creator: user2._id, content: 'Wha wha' }
+ ]
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .populate('comments._creator')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post._creator.name.should.equal('User 1');
+ post.comments[0]._creator.name.should.equal('User 1');
+ post.comments[1]._creator.name.should.equal('User 2');
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating subdocuments partially': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'User 1'
+ , email : 'user1@learnboost.com'
+ }, function (err, user1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'User 2'
+ , email : 'user2@learnboost.com'
+ }, function (err, user2) {
+ should.strictEqual(err, null);
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , comments: [
+ { _creator: user1, content: 'Woot woot' }
+ , { _creator: user2, content: 'Wha wha' }
+ ]
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('comments._creator', ['email'])
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post.comments[0]._creator.email.should.equal('user1@learnboost.com');
+ post.comments[0]._creator.isInit('name').should.be.false;
+ post.comments[1]._creator.email.should.equal('user2@learnboost.com');
+ post.comments[1]._creator.isInit('name').should.be.false;
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating subdocuments partially with conditions': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'User 1'
+ , email : 'user1@learnboost.com'
+ }, function (err, user1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'User 2'
+ , email : 'user2@learnboost.com'
+ }, function (err, user2) {
+ should.strictEqual(err, null);
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , comments: [
+ { _creator: user1, content: 'Woot woot' }
+ , { _creator: user2, content: 'Wha wha' }
+ ]
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('comments._creator', {'email': 1}, { name: /User/ })
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post.comments[0]._creator.email.should.equal('user1@learnboost.com');
+ post.comments[0]._creator.isInit('name').should.be.false;
+ post.comments[1]._creator.email.should.equal('user2@learnboost.com');
+ post.comments[1]._creator.isInit('name').should.be.false;
+ });
+ });
+ });
+ });
+ },
+
+ 'populating subdocs with invalid/missing subproperties': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'T-100'
+ , email : 'terminator100@learnboost.com'
+ }, function (err, user1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'T-1000'
+ , email : 'terminator1000@learnboost.com'
+ }, function (err, user2) {
+ should.strictEqual(err, null);
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , comments: [
+ { _creator: null, content: 'Woot woot' }
+ , { _creator: user2, content: 'Wha wha' }
+ ]
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ // invalid subprop
+ BlogPost
+ .findById(post._id)
+ .populate('comments._idontexist', 'email')
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+ should.exist(post);
+ post.comments.length.should.equal(2);
+ should.strictEqual(post.comments[0]._creator, null);
+ post.comments[1]._creator.toString().should.equal(user2.id);
+
+ // subprop is null in a doc
+ BlogPost
+ .findById(post._id)
+ .populate('comments._creator', 'email')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ should.exist(post);
+ post.comments.length.should.equal(2);
+ should.strictEqual(post.comments[0]._creator, null);
+ should.strictEqual(post.comments[0].content, 'Woot woot');
+ post.comments[1]._creator.email.should.equal('terminator1000@learnboost.com');
+ post.comments[1]._creator.isInit('name').should.be.false;
+ post.comments[1].content.should.equal('Wha wha');
+ });
+ });
+ });
+ });
+ });
+ },
+
+ // gh-481
+ 'test populating subdocuments partially with empty array': function (beforeExit) {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , worked = false;
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , comments: [] // EMPTY ARRAY
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('comments._creator', ['email'])
+ .run(function (err, returned) {
+ db.close();
+ worked = true;
+ should.strictEqual(err, null);
+ returned.id.should.equal(post.id);
+ });
+ });
+
+ beforeExit(function () {
+ worked.should.be.true;
+ });
+ },
+
+ 'populating subdocuments with array including nulls': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users)
+
+ var user = new User({ name: 'hans zimmer' });
+ user.save(function (err) {
+ should.strictEqual(err, null);
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , fans: []
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ // shove some uncasted vals
+ BlogPost.collection.update({ _id: post._id }, { $set: { fans: [null, undefined, user.id, null] } }, function (err) {
+ should.strictEqual(err, undefined);
+
+ BlogPost
+ .findById(post._id)
+ .populate('fans', ['name'])
+ .run(function (err, returned) {
+ db.close();
+ should.strictEqual(err, null);
+ returned.id.should.equal(post.id);
+ returned.fans.length.should.equal(1);
+ });
+ })
+ });
+ });
+ },
+
+ 'populating more than one array at a time': function () {
+ var db = start()
+ , User = db.model('RefUser', users)
+ , M = db.model('PopMultiSubDocs', new Schema({
+ users: [{ type: ObjectId, ref: 'RefUser' }]
+ , fans: [{ type: ObjectId, ref: 'RefUser' }]
+ , comments: [Comment]
+ }))
+
+ User.create({
+ email : 'fan1@learnboost.com'
+ }, {
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ , gender : 'female'
+ }, {
+ name: 'Fan 3'
+ }, function (err, fan1, fan2, fan3) {
+ should.strictEqual(err, null);
+
+ M.create({
+ users: [fan3]
+ , fans: [fan1]
+ , comments: [
+ { _creator: fan1, content: 'bejeah!' }
+ , { _creator: fan2, content: 'chickfila' }
+ ]
+ }, {
+ users: [fan1]
+ , fans: [fan2]
+ , comments: [
+ { _creator: fan3, content: 'hello' }
+ , { _creator: fan1, content: 'world' }
+ ]
+ }, function (err, post1, post2) {
+ should.strictEqual(err, null);
+
+ M.where('_id').in([post1, post2])
+ .populate('fans', 'name', { gender: 'female' })
+ .populate('users', 'name', { gender: 'male' })
+ .populate('comments._creator', ['email'], { name: null })
+ .run(function (err, posts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ should.exist(posts);
+ posts.length.should.equal(2);
+ var p1 = posts[0];
+ var p2 = posts[1];
+ should.strictEqual(p1.fans.length, 0);
+ should.strictEqual(p2.fans.length, 1);
+ p2.fans[0].name.should.equal('Fan 2');
+ p2.fans[0].isInit('email').should.be.false;
+ p2.fans[0].isInit('gender').should.be.false;
+ p1.comments.length.should.equal(2);
+ p2.comments.length.should.equal(2);
+ should.exist(p1.comments[0]._creator.email);
+ should.not.exist(p2.comments[0]._creator);
+ p1.comments[0]._creator.email.should.equal('fan1@learnboost.com');
+ p2.comments[1]._creator.email.should.equal('fan1@learnboost.com');
+ p1.comments[0]._creator.isInit('name').should.be.false;
+ p2.comments[1]._creator.isInit('name').should.be.false;
+ p1.comments[0].content.should.equal('bejeah!');
+ p2.comments[1].content.should.equal('world');
+ should.not.exist(p1.comments[1]._creator);
+ should.not.exist(p2.comments[0]._creator);
+ p1.comments[1].content.should.equal('chickfila');
+ p2.comments[0].content.should.equal('hello');
+ });
+ });
+ });
+ },
+
+ 'populating multiple children of a sub-array at a time': function () {
+ var db = start()
+ , User = db.model('RefUser', users)
+ , BlogPost = db.model('RefBlogPost', posts)
+ , Inner = new Schema({
+ user: { type: ObjectId, ref: 'RefUser' }
+ , post: { type: ObjectId, ref: 'RefBlogPost' }
+ })
+ , I = db.model('PopMultiChildrenOfSubDocInner', Inner)
+ var M = db.model('PopMultiChildrenOfSubDoc', new Schema({
+ kids: [Inner]
+ }))
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ , gender : 'male'
+ }, {
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ , gender : 'female'
+ }, function (err, fan1, fan2) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ }, {
+ title : 'yay'
+ }, function (err, post1, post2) {
+ should.strictEqual(err, null);
+ M.create({
+ kids: [
+ { user: fan1, post: post1, y: 5 }
+ , { user: fan2, post: post2, y: 8 }
+ ]
+ , x: 4
+ }, function (err, m1) {
+ should.strictEqual(err, null);
+
+ M.findById(m1)
+ .populate('kids.user', ["name"])
+ .populate('kids.post', ["title"], { title: "woot" })
+ .run(function (err, o) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(o.kids.length, 2);
+ var k1 = o.kids[0];
+ var k2 = o.kids[1];
+ should.strictEqual(true, !k2.post);
+ should.strictEqual(k1.user.name, "Fan 1");
+ should.strictEqual(k1.user.email, undefined);
+ should.strictEqual(k1.post.title, "woot");
+ should.strictEqual(k2.user.name, "Fan 2");
+ });
+ });
+ });
+ });
+ },
+
+ 'passing sort options to the populate method': function () {
+ var db = start()
+ , P = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({ name: 'aaron', age: 10 }, { name: 'fan2', age: 8 }, { name: 'someone else', age: 3 },
+ function (err, fan1, fan2, fan3) {
+ should.strictEqual(err, null);
+
+ P.create({ fans: [fan2, fan3, fan1] }, function (err, post) {
+ should.strictEqual(err, null);
+
+ P.findById(post)
+ .populate('fans', null, null, { sort: 'name' })
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+
+ post.fans.length.should.equal(3);
+ post.fans[0].name.should.equal('aaron');
+ post.fans[1].name.should.equal('fan2');
+ post.fans[2].name.should.equal('someone else');
+
+ P.findById(post)
+ .populate('fans', 'name', null, { sort: [['name', -1]] })
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+
+ post.fans.length.should.equal(3);
+ post.fans[2].name.should.equal('aaron');
+ should.strictEqual(undefined, post.fans[2].age)
+ post.fans[1].name.should.equal('fan2');
+ should.strictEqual(undefined, post.fans[1].age)
+ post.fans[0].name.should.equal('someone else');
+ should.strictEqual(undefined, post.fans[0].age)
+
+ P.findById(post)
+ .populate('fans', 'age', { age: { $gt: 3 }}, { sort: [['name', 'desc']] })
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post.fans.length.should.equal(2);
+ post.fans[1].age.valueOf().should.equal(10);
+ post.fans[0].age.valueOf().should.equal(8);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'refs should cast to ObjectId from hexstrings': function () {
+ var BP = mongoose.model('RefBlogPost', BlogPost);
+ var bp = new BP;
+ bp._creator = new DocObjectId().toString();
+ bp._creator.should.be.an.instanceof(DocObjectId);
+ bp.set('_creator', new DocObjectId().toString());
+ bp._creator.should.be.an.instanceof(DocObjectId);
+ },
+
+ 'populate should work on String _ids': function () {
+ var db = start();
+
+ var UserSchema = new Schema({
+ _id: String
+ , name: String
+ })
+
+ var NoteSchema = new Schema({
+ author: { type: String, ref: 'UserWithStringId' }
+ , body: String
+ })
+
+ var User = db.model('UserWithStringId', UserSchema, random())
+ var Note = db.model('NoteWithStringId', NoteSchema, random())
+
+ var alice = new User({_id: 'alice', name: "Alice"})
+
+ alice.save(function (err) {
+ should.strictEqual(err, null);
+
+ var note = new Note({author: 'alice', body: "Buy Milk"});
+ note.save(function (err) {
+ should.strictEqual(err, null);
+
+ Note.findById(note.id).populate('author').run(function (err, note) {
+ db.close();
+ should.strictEqual(err, null);
+ note.body.should.equal('Buy Milk');
+ should.exist(note.author);
+ note.author.name.should.equal('Alice');
+ });
+ });
+ })
+ },
+
+ 'populate should work on Number _ids': function () {
+ var db = start();
+
+ var UserSchema = new Schema({
+ _id: Number
+ , name: String
+ })
+
+ var NoteSchema = new Schema({
+ author: { type: Number, ref: 'UserWithNumberId' }
+ , body: String
+ })
+
+ var User = db.model('UserWithNumberId', UserSchema, random())
+ var Note = db.model('NoteWithNumberId', NoteSchema, random())
+
+ var alice = new User({_id: 2359, name: "Alice"})
+
+ alice.save(function (err) {
+ should.strictEqual(err, null);
+
+ var note = new Note({author: 2359, body: "Buy Milk"});
+ note.save(function (err) {
+ should.strictEqual(err, null);
+
+ Note.findById(note.id).populate('author').run(function (err, note) {
+ db.close();
+ should.strictEqual(err, null);
+ note.body.should.equal('Buy Milk');
+ should.exist(note.author);
+ note.author.name.should.equal('Alice');
+ });
+ });
+ })
+ },
+
+ // gh-577
+ 'required works on ref fields': function () {
+ var db = start();
+
+ var userSchema = new Schema({
+ email: {type: String, required: true}
+ });
+ var User = db.model('ObjectIdRefRequiredField', userSchema, random());
+
+ var numSchema = new Schema({ _id: Number, val: Number });
+ var Num = db.model('NumberRefRequired', numSchema, random());
+
+ var strSchema = new Schema({ _id: String, val: String });
+ var Str = db.model('StringRefRequired', strSchema, random());
+
+ var commentSchema = new Schema({
+ user: {type: ObjectId, ref: 'ObjectIdRefRequiredField', required: true}
+ , num: {type: Number, ref: 'NumberRefRequired', required: true}
+ , str: {type: String, ref: 'StringRefRequired', required: true}
+ , text: String
+ });
+ var Comment = db.model('CommentWithRequiredField', commentSchema);
+
+ var pending = 3;
+
+ var string = new Str({ _id: 'my string', val: 'hello' });
+ var number = new Num({ _id: 1995, val: 234 });
+ var user = new User({ email: 'test' });
+
+ string.save(next);
+ number.save(next);
+ user.save(next);
+
+ function next (err) {
+ should.strictEqual(null, err);
+ if (--pending) return;
+
+ var comment = new Comment({
+ text: 'test'
+ });
+
+ comment.save(function (err) {
+ should.equal('Validation failed', err && err.message);
+ err.errors.should.have.property('num');
+ err.errors.should.have.property('str');
+ err.errors.should.have.property('user');
+ err.errors.num.type.should.equal('required');
+ err.errors.str.type.should.equal('required');
+ err.errors.user.type.should.equal('required');
+
+ comment.user = user;
+ comment.num = 1995;
+ comment.str = 'my string';
+
+ comment.save(function (err, comment) {
+ should.strictEqual(null, err);
+
+ Comment
+ .findById(comment.id)
+ .populate('user')
+ .populate('num')
+ .populate('str')
+ .run(function (err, comment) {
+ should.strictEqual(err, null);
+
+ comment.set({text: 'test2'});
+
+ comment.save(function (err, comment) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ });
+ });
+ }
+ },
+
+ 'populate works with schemas with both id and _id defined': function () {
+ var db =start()
+ , S1 = new Schema({ id: String })
+ , S2 = new Schema({ things: [{ type: ObjectId, ref: '_idAndid' }]})
+
+ var M1 = db.model('_idAndid', S1);
+ var M2 = db.model('populateWorksWith_idAndidSchemas', S2);
+
+ M1.create(
+ { id: "The Tiger That Isn't" }
+ , { id: "Users Guide To The Universe" }
+ , function (err, a, b) {
+ should.strictEqual(null, err);
+
+ var m2 = new M2({ things: [a, b]});
+ m2.save(function (err) {
+ should.strictEqual(null, err);
+ M2.findById(m2).populate('things').run(function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.things.length.should.equal(2);
+ doc.things[0].id.should.equal("The Tiger That Isn't");
+ doc.things[1].id.should.equal("Users Guide To The Universe");
+ })
+ });
+ })
+ },
+
+ // gh-602
+ 'Update works with populated arrays': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users)
+
+ var user1 = new User({ name: 'aphex' });
+ var user2 = new User({ name: 'twin' });
+
+ User.create({name:'aphex'},{name:'twin'}, function (err, u1, u2) {
+ should.strictEqual(err, null);
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , fans: []
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ var update = { fans: [u1, u2] };
+ BlogPost.update({ _id: post }, update, function (err) {
+ should.strictEqual(err, null);
+
+ // the original update doc should not be modified
+ ;('fans' in update).should.be.true;
+ ;('$set' in update).should.be.false;
+ update.fans[0].should.be.instanceof(mongoose.Document);
+ update.fans[1].should.be.instanceof(mongoose.Document);
+
+ BlogPost.findById(post, function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+ post.fans.length.should.equal(2);
+ post.fans[0].should.be.instanceof(DocObjectId);
+ post.fans[1].should.be.instanceof(DocObjectId);
+ });
+ });
+ });
+ });
+ },
+
+ // gh-675
+ 'toJSON should also be called for refs': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users)
+
+ User.prototype._toJSON = User.prototype.toJSON;
+ User.prototype.toJSON = function() {
+ var res = this._toJSON();
+ res.was_in_to_json = true;
+ return res;
+ }
+
+ BlogPost.prototype._toJSON = BlogPost.prototype.toJSON;
+ BlogPost.prototype.toJSON = function() {
+ var res = this._toJSON();
+ res.was_in_to_json = true;
+ return res;
+ }
+
+ User.create({
+ name : 'Jerem'
+ , email : 'jerem@jolicloud.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Ping Pong'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ var json = post.toJSON();
+ json.was_in_to_json.should.equal(true);
+ json._creator.was_in_to_json.should.equal(true);
+ });
+ });
+ });
+ },
+
+ // gh-686
+ 'populate should work on Buffer _ids': function () {
+ var db = start();
+
+ var UserSchema = new Schema({
+ _id: Buffer
+ , name: String
+ })
+
+ var NoteSchema = new Schema({
+ author: { type: Buffer, ref: 'UserWithBufferId' }
+ , body: String
+ })
+
+ var User = db.model('UserWithBufferId', UserSchema, random())
+ var Note = db.model('NoteWithBufferId', NoteSchema, random())
+
+ var alice = new User({_id: new mongoose.Types.Buffer('YWxpY2U=', 'base64'), name: "Alice"})
+
+ alice.save(function (err) {
+ should.strictEqual(err, null);
+
+ var note = new Note({author: 'alice', body: "Buy Milk"});
+ note.save(function (err) {
+ should.strictEqual(err, null);
+
+ Note.findById(note.id).populate('author').run(function (err, note) {
+ db.close();
+ should.strictEqual(err, null);
+ note.body.should.equal('Buy Milk');
+ should.exist(note.author);
+ note.author.name.should.equal('Alice');
+ });
+ });
+ })
+ }
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/test/model.stream.test.js b/node_modules/mongoose/test/model.stream.test.js
new file mode 100644
index 0000000..a010b0e
--- /dev/null
+++ b/node_modules/mongoose/test/model.stream.test.js
@@ -0,0 +1,232 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , utils = require('../lib/utils')
+ , random = utils.random
+ , Query = require('../lib/query')
+ , Schema = mongoose.Schema
+ , SchemaType = mongoose.SchemaType
+ , CastError = SchemaType.CastError
+ , ObjectId = Schema.ObjectId
+ , MongooseBuffer = mongoose.Types.Buffer
+ , DocumentObjectId = mongoose.Types.ObjectId
+ , fs = require('fs')
+
+var names = ('Aaden Aaron Adrian Aditya Agustin Jim Bob Jonah Frank Sally Lucy').split(' ');
+
+/**
+ * Setup.
+ */
+
+var Person = new Schema({
+ name: String
+});
+
+mongoose.model('PersonForStream', Person);
+var collection = 'personforstream_' + random();
+
+;(function setup () {
+ var db = start()
+ , P = db.model('PersonForStream', collection)
+
+ var people = names.map(function (name) {
+ return { name: name };
+ });
+
+ P.create(people, function (err) {
+ should.strictEqual(null, err);
+ db.close();
+ assignExports();
+ });
+})()
+
+function assignExports () { var o = {
+
+ 'cursor stream': function () {
+ var db = start()
+ , P = db.model('PersonForStream', collection)
+ , i = 0
+ , closed = 0
+ , paused = 0
+ , resumed = 0
+ , err
+
+ var stream = P.find({}).stream();
+
+ stream.on('data', function (doc) {
+ should.strictEqual(true, !! doc.name);
+ should.strictEqual(true, !! doc._id);
+
+ if (paused > 0 && 0 === resumed) {
+ err = new Error('data emitted during pause');
+ return done();
+ }
+
+ if (++i === 3) {
+ stream.paused.should.be.false;
+ stream.pause();
+ stream.paused.should.be.true;
+ paused++;
+
+ setTimeout(function () {
+ stream.paused.should.be.true;
+ resumed++;
+ stream.resume();
+ stream.paused.should.be.false;
+ }, 20);
+ }
+ });
+
+ stream.on('error', function (er) {
+ err = er;
+ done();
+ });
+
+ stream.on('close', function () {
+ closed++;
+ done();
+ });
+
+ function done () {
+ db.close();
+ should.strictEqual(undefined, err);
+ should.equal(i, names.length);
+ closed.should.equal(1);
+ paused.should.equal(1);
+ resumed.should.equal(1);
+ stream._cursor.isClosed().should.be.true;
+ }
+ }
+
+, 'immediately destroying a stream prevents the query from executing': function () {
+ var db = start()
+ , P = db.model('PersonForStream', collection)
+ , i = 0
+
+ var stream = P.where('name', 'Jonah').select('name').findOne().stream();
+
+ stream.on('data', function () {
+ i++;
+ })
+ stream.on('close', done);
+ stream.on('error', done);
+
+ stream.destroy();
+
+ function done (err) {
+ should.strictEqual(undefined, err);
+ i.should.equal(0);
+ process.nextTick(function () {
+ db.close();
+ should.strictEqual(null, stream._fields);
+ })
+ }
+ }
+
+, 'destroying a stream stops it': function () {
+ var db = start()
+ , P = db.model('PersonForStream', collection)
+ , finished = 0
+ , i = 0
+
+ var stream = P.where('name').$exists().limit(10).only('_id').stream();
+
+ should.strictEqual(null, stream._destroyed);
+ stream.readable.should.be.true;
+
+ stream.on('data', function (doc) {
+ should.strictEqual(undefined, doc.name);
+ if (++i === 5) {
+ stream.destroy();
+ stream.readable.should.be.false;
+ }
+ });
+
+ stream.on('close', done);
+ stream.on('error', done);
+
+ function done (err) {
+ ++finished;
+ setTimeout(function () {
+ db.close();
+ should.strictEqual(undefined, err);
+ i.should.equal(5);
+ finished.should.equal(1);
+ stream._destroyed.should.equal(true);
+ stream.readable.should.be.false;
+ stream._cursor.isClosed().should.be.true;
+ }, 150)
+ }
+ }
+
+, 'cursor stream errors': function () {
+ var db = start({ server: { auto_reconnect: false }})
+ , P = db.model('PersonForStream', collection)
+ , finished = 0
+ , closed = 0
+ , i = 0
+
+ var stream = P.find().batchSize(5).stream();
+
+ stream.on('data', function (doc) {
+ if (++i === 5) {
+ db.close();
+ }
+ });
+
+ stream.on('close', function () {
+ closed++;
+ });
+
+ stream.on('error', done);
+
+ function done (err) {
+ ++finished;
+ setTimeout(function () {
+ should.equal('no open connections', err.message);
+ i.should.equal(5);
+ closed.should.equal(1);
+ finished.should.equal(1);
+ stream._destroyed.should.equal(true);
+ stream.readable.should.be.false;
+ stream._cursor.isClosed().should.be.true;
+ }, 150)
+ }
+ }
+
+, 'cursor stream pipe': function () {
+ var db = start()
+ , P = db.model('PersonForStream', collection)
+ , filename = '/tmp/_mongoose_stream_out.txt'
+ , out = fs.createWriteStream(filename)
+
+ var stream = P.find().sort('name', 1).limit(20).stream();
+ stream.pipe(out);
+
+ stream.on('error', done);
+ stream.on('close', done);
+
+ function done (err) {
+ db.close();
+ should.strictEqual(undefined, err);
+ var contents = fs.readFileSync(filename, 'utf8');
+ ;/Aaden/.test(contents).should.be.true;
+ ;/Aaron/.test(contents).should.be.true;
+ ;/Adrian/.test(contents).should.be.true;
+ ;/Aditya/.test(contents).should.be.true;
+ ;/Agustin/.test(contents).should.be.true;
+ fs.unlink(filename);
+ }
+ }
+}
+
+// end exports
+
+utils.merge(exports, o);
+
+}
diff --git a/node_modules/mongoose/test/model.test.js b/node_modules/mongoose/test/model.test.js
new file mode 100644
index 0000000..22c7433
--- /dev/null
+++ b/node_modules/mongoose/test/model.test.js
@@ -0,0 +1,4682 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Query = require('../lib/query')
+ , Schema = mongoose.Schema
+ , SchemaType = mongoose.SchemaType
+ , CastError = SchemaType.CastError
+ , ValidatorError = SchemaType.ValidatorError
+ , ValidationError = mongoose.Document.ValidationError
+ , ObjectId = Schema.ObjectId
+ , DocumentObjectId = mongoose.Types.ObjectId
+ , DocumentArray = mongoose.Types.DocumentArray
+ , EmbeddedDocument = mongoose.Types.Embedded
+ , MongooseNumber = mongoose.Types.Number
+ , MongooseArray = mongoose.Types.Array
+ , MongooseError = mongoose.Error;
+
+/**
+ * Setup.
+ */
+
+var Comments = new Schema();
+
+Comments.add({
+ title : String
+ , date : Date
+ , body : String
+ , comments : [Comments]
+});
+
+var BlogPost = new Schema({
+ title : String
+ , author : String
+ , slug : String
+ , date : Date
+ , meta : {
+ date : Date
+ , visitors : Number
+ }
+ , published : Boolean
+ , mixed : {}
+ , numbers : [Number]
+ , owners : [ObjectId]
+ , comments : [Comments]
+});
+
+BlogPost.virtual('titleWithAuthor')
+ .get(function () {
+ return this.get('title') + ' by ' + this.get('author');
+ })
+ .set(function (val) {
+ var split = val.split(' by ');
+ this.set('title', split[0]);
+ this.set('author', split[1]);
+ });
+
+BlogPost.method('cool', function(){
+ return this;
+});
+
+BlogPost.static('woot', function(){
+ return this;
+});
+
+mongoose.model('BlogPost', BlogPost);
+
+var collection = 'blogposts_' + random();
+
+module.exports = {
+
+ 'test a model isNew flag when instantiating': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.isNew.should.be.true;
+ db.close();
+ },
+
+ 'modified getter should not throw': function () {
+ var db = start();
+ var BlogPost = db.model('BlogPost', collection);
+ var post = new BlogPost;
+ db.close();
+
+ var threw = false;
+ try {
+ post.modified;
+ } catch (err) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+ },
+
+ 'test presence of model schema': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.schema.should.be.an.instanceof(Schema);
+ BlogPost.prototype.schema.should.be.an.instanceof(Schema);
+ db.close();
+ },
+
+ 'test a model default structure when instantiated': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.isNew.should.be.true;
+ post.db.model('BlogPost').modelName.should.equal('BlogPost');
+ post.constructor.modelName.should.equal('BlogPost');
+
+ post.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ should.equal(undefined, post.get('title'));
+ should.equal(undefined, post.get('slug'));
+ should.equal(undefined, post.get('date'));
+
+ post.get('meta').should.be.a('object');
+ post.get('meta').should.eql({});
+ should.equal(undefined, post.get('meta.date'));
+ should.equal(undefined, post.get('meta.visitors'));
+
+ should.equal(undefined, post.get('published'));
+
+ post.get('numbers').should.be.an.instanceof(MongooseArray);
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ db.close();
+ },
+
+ 'mongoose.model returns the model at creation': function () {
+ var Named = mongoose.model('Named', new Schema({ name: String }));
+ var n1 = new Named();
+ should.equal(n1.name, null);
+ var n2 = new Named({ name: 'Peter Bjorn' });
+ should.equal(n2.name, 'Peter Bjorn');
+
+ var schema = new Schema({ number: Number });
+ var Numbered = mongoose.model('Numbered', schema, collection);
+ var n3 = new Numbered({ number: 1234 });
+ n3.number.valueOf().should.equal(1234);
+ },
+
+ 'mongoose.model!init emits init event on schema': function () {
+ var db = start()
+ , schema = new Schema({ name: String })
+ , model
+
+ schema.on('init', function (model_) {
+ model = model_;
+ });
+
+ var Named = db.model('EmitInitOnSchema', schema);
+ db.close();
+ model.should.equal(Named);
+ },
+
+ 'collection name can be specified through schema': function () {
+ var schema = new Schema({ name: String }, { collection: 'users1' });
+ var Named = mongoose.model('CollectionNamedInSchema1', schema);
+ Named.prototype.collection.name.should.equal('users1');
+
+ var db = start();
+ var users2schema = new Schema({ name: String }, { collection: 'users2' });
+ var Named2 = db.model('CollectionNamedInSchema2', users2schema);
+ db.close();
+ Named2.prototype.collection.name.should.equal('users2');
+ },
+
+ 'test default Array type casts to Mixed': function () {
+ var db = start()
+ , DefaultArraySchema = new Schema({
+ num1: Array
+ , num2: []
+ })
+
+ mongoose.model('DefaultArraySchema', DefaultArraySchema);
+ var DefaultArray = db.model('DefaultArraySchema', collection);
+ var arr = new DefaultArray();
+
+ arr.get('num1').should.have.length(0);
+ arr.get('num2').should.have.length(0);
+
+ var threw1 = false
+ , threw2 = false;
+
+ try {
+ arr.num1.push({ x: 1 })
+ arr.num1.push(9)
+ arr.num1.push("woah")
+ } catch (err) {
+ threw1 = true;
+ }
+
+ threw1.should.equal(false);
+
+ try {
+ arr.num2.push({ x: 1 })
+ arr.num2.push(9)
+ arr.num2.push("woah")
+ } catch (err) {
+ threw2 = true;
+ }
+
+ threw2.should.equal(false);
+
+ db.close();
+
+ },
+
+ 'test a model default structure that has a nested Array when instantiated': function () {
+ var db = start()
+ , NestedSchema = new Schema({
+ nested: {
+ array: [Number]
+ }
+ });
+ mongoose.model('NestedNumbers', NestedSchema);
+ var NestedNumbers = db.model('NestedNumbers', collection);
+
+ var nested = new NestedNumbers();
+ nested.get('nested.array').should.be.an.instanceof(MongooseArray);
+ db.close();
+ },
+
+ 'test a model that defaults an Array attribute to a default non-empty array': function () {
+ var db = start()
+ , DefaultArraySchema = new Schema({
+ arr: {type: Array, cast: String, default: ['a', 'b', 'c']}
+ });
+ mongoose.model('DefaultArray', DefaultArraySchema);
+ var DefaultArray = db.model('DefaultArray', collection);
+ var arr = new DefaultArray();
+ arr.get('arr').should.have.length(3);
+ arr.get('arr')[0].should.equal('a');
+ arr.get('arr')[1].should.equal('b');
+ arr.get('arr')[2].should.equal('c');
+ db.close();
+ },
+
+ 'test a model that defaults an Array attribute to a default single member array': function () {
+ var db = start()
+ , DefaultOneCardArraySchema = new Schema({
+ arr: {type: Array, cast: String, default: ['a']}
+ });
+ mongoose.model('DefaultOneCardArray', DefaultOneCardArraySchema);
+ var DefaultOneCardArray = db.model('DefaultOneCardArray', collection);
+ var arr = new DefaultOneCardArray();
+ arr.get('arr').should.have.length(1);
+ arr.get('arr')[0].should.equal('a');
+ db.close();
+ },
+
+ 'test a model that defaults an Array attributes to an empty array': function () {
+ var db = start()
+ , DefaultZeroCardArraySchema = new Schema({
+ arr: {type: Array, cast: String, default: []}
+ });
+ mongoose.model('DefaultZeroCardArray', DefaultZeroCardArraySchema);
+ var DefaultZeroCardArray = db.model('DefaultZeroCardArray', collection);
+ var arr = new DefaultZeroCardArray();
+ arr.get('arr').should.have.length(0);
+ arr.arr.should.have.length(0);
+ db.close();
+ },
+
+ 'test that arrays auto-default to the empty array': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.numbers.should.have.length(0);
+ db.close();
+ },
+
+ 'test instantiating a model with a hash that maps to at least 1 null value': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({
+ title: null
+ });
+ should.strictEqual(null, post.title);
+ db.close();
+ },
+
+ 'saving a model with a null value should perpetuate that null value to the db': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({
+ title: null
+ });
+ should.strictEqual(null, post.title);
+ post.save( function (err) {
+ should.strictEqual(err, null);
+ BlogPost.findById(post.id, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(found.title, null);
+ });
+ });
+ },
+
+ 'test instantiating a model with a hash that maps to at least 1 undefined value': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({
+ title: undefined
+ });
+ should.strictEqual(undefined, post.title);
+ post.save( function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post.id, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(found.title, undefined);
+ });
+ });
+ },
+
+ 'test a model structure when saved': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , pending = 2;
+
+ function done () {
+ if (!--pending) db.close();
+ }
+
+ var post = new BlogPost();
+ post.on('save', function (post) {
+ post.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ should.equal(undefined, post.get('title'));
+ should.equal(undefined, post.get('slug'));
+ should.equal(undefined, post.get('date'));
+ should.equal(undefined, post.get('published'));
+
+ post.get('meta').should.be.a('object');
+ post.get('meta').should.eql({});
+ should.equal(undefined, post.get('meta.date'));
+ should.equal(undefined, post.get('meta.visitors'));
+
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ done();
+ });
+
+ post.save(function(err, post){
+ should.strictEqual(err, null);
+ post.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ should.equal(undefined, post.get('title'));
+ should.equal(undefined, post.get('slug'));
+ should.equal(undefined, post.get('date'));
+ should.equal(undefined, post.get('published'));
+
+ post.get('meta').should.be.a('object');
+ post.get('meta').should.eql({});
+ should.equal(undefined, post.get('meta.date'));
+ should.equal(undefined, post.get('meta.visitors'));
+
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ done();
+ });
+ },
+
+ 'test a model structures when created': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({ title: 'hi there'}, function (err, post) {
+ should.strictEqual(err, null);
+ post.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ should.strictEqual(post.get('title'), 'hi there');
+ should.equal(undefined, post.get('slug'));
+ should.equal(undefined, post.get('date'));
+
+ post.get('meta').should.be.a('object');
+ post.get('meta').should.eql({});
+ should.equal(undefined, post.get('meta.date'));
+ should.equal(undefined, post.get('meta.visitors'));
+
+ should.strictEqual(undefined, post.get('published'));
+
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ db.close();
+ });
+ },
+
+ 'test a model structure when initd': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , date : new Date
+ , meta : {
+ date : new Date
+ , visitors : 5
+ }
+ , published : true
+ , owners : [new DocumentObjectId, new DocumentObjectId]
+ , comments : [
+ { title: 'Test', date: new Date, body: 'Test' }
+ , { title: 'Super', date: new Date, body: 'Cool' }
+ ]
+ });
+
+ post.get('title').should.eql('Test');
+ post.get('slug').should.eql('test');
+ post.get('date').should.be.an.instanceof(Date);
+ post.get('meta').should.be.a('object');
+ post.get('meta').date.should.be.an.instanceof(Date);
+ post.get('meta').visitors.should.be.an.instanceof(MongooseNumber);
+ post.get('published').should.be.true;
+
+ post.title.should.eql('Test');
+ post.slug.should.eql('test');
+ post.date.should.be.an.instanceof(Date);
+ post.meta.should.be.a('object');
+ post.meta.date.should.be.an.instanceof(Date);
+ post.meta.visitors.should.be.an.instanceof(MongooseNumber);
+ post.published.should.be.true;
+
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('owners')[0].should.be.an.instanceof(DocumentObjectId);
+ post.get('owners')[1].should.be.an.instanceof(DocumentObjectId);
+
+ post.owners.should.be.an.instanceof(MongooseArray);
+ post.owners[0].should.be.an.instanceof(DocumentObjectId);
+ post.owners[1].should.be.an.instanceof(DocumentObjectId);
+
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ post.get('comments')[0].should.be.an.instanceof(EmbeddedDocument);
+ post.get('comments')[1].should.be.an.instanceof(EmbeddedDocument);
+
+ post.comments.should.be.an.instanceof(DocumentArray);
+ post.comments[0].should.be.an.instanceof(EmbeddedDocument);
+ post.comments[1].should.be.an.instanceof(EmbeddedDocument);
+
+ db.close();
+ },
+
+ 'test a model structure when partially initd': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , date : new Date
+ });
+
+ post.get('title').should.eql('Test');
+ post.get('slug').should.eql('test');
+ post.get('date').should.be.an.instanceof(Date);
+ post.get('meta').should.be.a('object');
+
+ post.get('meta').should.eql({});
+ should.equal(undefined, post.get('meta.date'));
+ should.equal(undefined, post.get('meta.visitors'));
+
+ should.equal(undefined, post.get('published'));
+
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ db.close();
+ },
+
+ 'test initializing with a nested hash': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({
+ meta: {
+ date : new Date
+ , visitors : 5
+ }
+ });
+
+ post.get('meta.visitors').valueOf().should.equal(5);
+ db.close();
+ },
+
+ 'test isNew on embedded documents after initing': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , comments : [ { title: 'Test', date: new Date, body: 'Test' } ]
+ });
+
+ post.get('comments')[0].isNew.should.be.false;
+ db.close();
+ },
+
+ 'test isNew on embedded documents after saving': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({ title: 'hocus pocus' })
+ post.comments.push({ title: 'Humpty Dumpty', comments: [{title: 'nested'}] });
+ post.get('comments')[0].isNew.should.be.true;
+ post.get('comments')[0].comments[0].isNew.should.be.true;
+ post.invalidate('title'); // force error
+ post.save(function (err) {
+ post.isNew.should.be.true;
+ post.get('comments')[0].isNew.should.be.true;
+ post.get('comments')[0].comments[0].isNew.should.be.true;
+ post.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ post.isNew.should.be.false;
+ post.get('comments')[0].isNew.should.be.false;
+ post.get('comments')[0].comments[0].isNew.should.be.false;
+ });
+ });
+ },
+
+ 'test isNew on embedded documents added after saving': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({ title: 'hocus pocus' })
+ post.save(function (err) {
+ post.isNew.should.be.false;
+ post.comments.push({ title: 'Humpty Dumpty', comments: [{title: 'nested'}] });
+ post.get('comments')[0].isNew.should.be.true;
+ post.get('comments')[0].comments[0].isNew.should.be.true;
+ post.save(function (err) {
+ should.strictEqual(null, err);
+ post.isNew.should.be.false;
+ post.get('comments')[0].isNew.should.be.false;
+ post.get('comments')[0].comments[0].isNew.should.be.false;
+ db.close();
+ });
+ });
+ },
+
+ 'modified states are reset after save runs': function () {
+ var db = start()
+ , B = db.model('BlogPost', collection)
+ , pending = 2;
+
+ var b = new B;
+
+ b.numbers.push(3);
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ --pending || find();
+ });
+
+ b.numbers.push(3);
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ --pending || find();
+ });
+
+ function find () {
+ B.findById(b, function (err, b) {
+ db.close();
+ should.strictEqual(null, err);
+ b.numbers.length.should.equal(2);
+ });
+ }
+ },
+
+ 'modified states in emb-doc are reset after save runs': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({ title: 'hocus pocus' });
+ post.comments.push({ title: 'Humpty Dumpty', comments: [{title: 'nested'}] });
+ post.save(function(err){
+ db.close();
+ should.strictEqual(null, err);
+ var mFlag = post.comments[0].isModified('title');
+ mFlag.should.equal(false);
+ post.isModified('title').should.equal(false);
+ });
+
+ },
+
+ 'test isModified when modifying keys': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , date : new Date
+ });
+
+ post.isModified('title').should.be.false;
+ post.set('title', 'test');
+ post.isModified('title').should.be.true;
+
+ post.isModified('date').should.be.false;
+ post.set('date', new Date(post.date + 1));
+ post.isModified('date').should.be.true;
+
+ post.isModified('meta.date').should.be.false;
+ db.close();
+ },
+
+ 'setting a key identically to its current value should not dirty the key': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , date : new Date
+ });
+
+ post.isModified('title').should.be.false;
+ post.set('title', 'Test');
+ post.isModified('title').should.be.false;
+ db.close();
+ },
+
+ 'test isModified and isDirectModified on a DocumentArray': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , comments : [ { title: 'Test', date: new Date, body: 'Test' } ]
+ });
+
+ post.isModified('comments.0.title').should.be.false;
+ post.get('comments')[0].set('title', 'Woot');
+ post.isModified('comments').should.be.true;
+ post.isDirectModified('comments').should.be.false;
+ post.isModified('comments.0.title').should.be.true;
+ post.isDirectModified('comments.0.title').should.be.true;
+
+ db.close();
+ },
+
+ 'test isModified on a DocumentArray with accessors': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , comments : [ { title: 'Test', date: new Date, body: 'Test' } ]
+ });
+
+ post.isModified('comments.0.body').should.be.false;
+ post.get('comments')[0].body = 'Woot';
+ post.isModified('comments').should.be.true;
+ post.isDirectModified('comments').should.be.false;
+ post.isModified('comments.0.body').should.be.true;
+ post.isDirectModified('comments.0.body').should.be.true;
+
+ db.close();
+ },
+
+ 'test isModified on a MongooseArray with atomics methods': function(){
+ // COMPLETEME
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+
+ post.isModified('owners').should.be.false;
+ post.get('owners').$push(new DocumentObjectId);
+ post.isModified('owners').should.be.true;
+
+ db.close();
+ },
+
+ 'test isModified on a MongooseArray with native methods': function(){
+ // COMPLETEME
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+
+ post.isModified('owners').should.be.false;
+ post.get('owners').push(new DocumentObjectId);
+
+ db.close();
+ },
+
+ 'test isModified on a Mongoose Document - Modifying an existing record' : function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+
+ var doc = {
+ title : 'Test'
+ , slug : 'test'
+ , date : new Date
+ , meta : {
+ date : new Date
+ , visitors : 5
+ }
+ , published : true
+ , mixed : { x: [ { y: [1,'yes', 2] } ] }
+ , numbers : []
+ , owners : [new DocumentObjectId, new DocumentObjectId]
+ , comments : [
+ { title: 'Test', date: new Date, body: 'Test' }
+ , { title: 'Super', date: new Date, body: 'Cool' }
+ ]
+ };
+
+ BlogPost.create(doc, function (err, post) {
+ BlogPost.findById(post.id, function (err, postRead) {
+ db.close();
+ should.strictEqual(null, err);
+ //set the same data again back to the document.
+ //expected result, nothing should be set to modified
+ postRead.isModified('comments').should.be.false;
+ postRead.isNew.should.be.false;
+ postRead.set(postRead.toObject());
+
+ postRead.isModified('title').should.be.false;
+ postRead.isModified('slug').should.be.false;
+ postRead.isModified('date').should.be.false;
+ postRead.isModified('meta.date').should.be.false;
+ postRead.isModified('meta.visitors').should.be.false;
+ postRead.isModified('published').should.be.false;
+ postRead.isModified('mixed').should.be.false;
+ postRead.isModified('numbers').should.be.false;
+ postRead.isModified('owners').should.be.false;
+ postRead.isModified('comments').should.be.false;
+ postRead.comments[2] = { title: 'index' };
+ postRead.comments = postRead.comments;
+ postRead.isModified('comments').should.be.true;
+ });
+ });
+ },
+
+ // gh-662
+ 'test nested structure created by merging': function() {
+ var db = start();
+
+ var MergedSchema = new Schema({
+ a: {
+ foo: String
+ }
+ });
+
+ MergedSchema.add({
+ a: {
+ b: {
+ bar: String
+ }
+ }
+ });
+
+ mongoose.model('Merged', MergedSchema);
+ var Merged = db.model('Merged', 'merged_' + Math.random());
+
+ var merged = new Merged({
+ a: {
+ foo: 'baz'
+ , b: {
+ bar: 'qux'
+ }
+ }
+ });
+
+ merged.save(function(err) {
+ should.strictEqual(null, err);
+ Merged.findById(merged.id, function(err, found) {
+ db.close();
+ should.strictEqual(null, err);
+ found.a.foo.should.eql('baz');
+ found.a.b.bar.should.eql('qux');
+ });
+ });
+ },
+
+ // gh-714
+ 'modified nested objects which contain MongoseNumbers should not cause a RangeError on save': function () {
+ var db =start()
+
+ var schema = new Schema({
+ nested: {
+ num: Number
+ }
+ });
+
+ var M = db.model('NestedObjectWithMongooseNumber', schema);
+ var m = new M;
+ m.nested = null;
+ m.save(function (err) {
+ should.strictEqual(null, err);
+
+ M.findById(m, function (err, m) {
+ should.strictEqual(null, err);
+ m.nested.num = 5;
+ m.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ },
+
+ // gh-714 pt 2
+ 'no RangeError on remove() of a doc with Number _id': function () {
+ var db = start()
+
+ var MySchema = new Schema({
+ _id: { type: Number },
+ name: String
+ });
+
+ var MyModel = db.model('MyModel', MySchema, 'numberrangeerror'+random());
+
+ var instance = new MyModel({
+ name: 'test'
+ , _id: 35
+ });
+
+ instance.save(function (err) {
+ should.strictEqual(null, err);
+
+ MyModel.findById(35, function (err, doc) {
+ should.strictEqual(null, err);
+
+ doc.remove(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ },
+
+ // GH-342
+ 'over-writing a number should persist to the db': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({
+ meta: {
+ date : new Date
+ , visitors : 10
+ }
+ });
+
+ post.save( function (err) {
+ should.strictEqual(null, err);
+ post.set('meta.visitors', 20);
+ post.save( function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post.id, function (err, found) {
+ should.strictEqual(null, err);
+ found.get('meta.visitors').valueOf().should.equal(20);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test defining a new method': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.cool().should.eql(post);
+ db.close();
+ },
+
+ 'test defining a static': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.woot().should.eql(BlogPost);
+ db.close();
+ },
+
+ 'test casting error': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , threw = false;
+
+ var post = new BlogPost();
+
+ try {
+ post.init({
+ date: 'Test'
+ });
+ } catch(e){
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ try {
+ post.set('title', 'Test');
+ } catch(e){
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(CastError);
+ db.close();
+ });
+ },
+
+ 'test nested casting error': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , threw = false;
+
+ var post = new BlogPost();
+
+ try {
+ post.init({
+ meta: {
+ date: 'Test'
+ }
+ });
+ } catch(e){
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ try {
+ post.set('meta.date', 'Test');
+ } catch(e){
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(CastError);
+ db.close();
+ });
+ },
+
+ 'test casting error in subdocuments': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , threw = false;
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , comments : [ { title: 'Test', date: new Date, body: 'Test' } ]
+ });
+
+ post.get('comments')[0].set('date', 'invalid');
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(CastError);
+ db.close();
+ });
+ },
+
+ 'test casting error when adding a subdocument': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , threw = false;
+
+ var post = new BlogPost()
+
+ try {
+ post.get('comments').push({
+ date: 'Bad date'
+ });
+ } catch (e) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(CastError);
+ db.close();
+ });
+ },
+
+ 'test validation': function(){
+ function dovalidate (val) {
+ this.asyncScope.should.equal('correct');
+ return true;
+ }
+
+ function dovalidateAsync (val, callback) {
+ this.scope.should.equal('correct');
+ process.nextTick(function () {
+ callback(true);
+ });
+ }
+
+ mongoose.model('TestValidation', new Schema({
+ simple: { type: String, required: true }
+ , scope: { type: String, validate: [dovalidate, 'scope failed'], required: true }
+ , asyncScope: { type: String, validate: [dovalidateAsync, 'async scope failed'], required: true }
+ }));
+
+ var db = start()
+ , TestValidation = db.model('TestValidation');
+
+ var post = new TestValidation();
+ post.set('simple', '');
+ post.set('scope', 'correct');
+ post.set('asyncScope', 'correct');
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ post.set('simple', 'here');
+ post.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ 'test validation with custom message': function () {
+ function validate (val) {
+ return val === 'abc';
+ }
+ mongoose.model('TestValidationMessage', new Schema({
+ simple: { type: String, validate: [validate, 'must be abc'] }
+ }));
+
+ var db = start()
+ , TestValidationMessage = db.model('TestValidationMessage');
+
+ var post = new TestValidationMessage();
+ post.set('simple', '');
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ err.errors.simple.should.be.an.instanceof(ValidatorError);
+ err.errors.simple.message.should.equal('Validator "must be abc" failed for path simple');
+ post.errors.simple.message.should.equal('Validator "must be abc" failed for path simple');
+
+ post.set('simple', 'abc');
+ post.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ // GH-272
+ 'test validation with Model.schema.path introspection': function () {
+ var db = start();
+ var IntrospectionValidationSchema = new Schema({
+ name: String
+ });
+ var IntrospectionValidation = db.model('IntrospectionValidation', IntrospectionValidationSchema, 'introspections_' + random());
+ IntrospectionValidation.schema.path('name').validate(function (value) {
+ return value.length < 2;
+ }, 'Name cannot be greater than 1 character');
+ var doc = new IntrospectionValidation({name: 'hi'});
+ doc.save( function (err) {
+ err.errors.name.message.should.equal("Validator \"Name cannot be greater than 1 character\" failed for path name");
+ err.name.should.equal("ValidationError");
+ err.message.should.equal("Validation failed");
+ db.close();
+ });
+ },
+
+ 'test required validation for undefined values': function () {
+ mongoose.model('TestUndefinedValidation', new Schema({
+ simple: { type: String, required: true }
+ }));
+
+ var db = start()
+ , TestUndefinedValidation = db.model('TestUndefinedValidation');
+
+ var post = new TestUndefinedValidation();
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ post.set('simple', 'here');
+ post.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ // GH-319
+ 'save callback should only execute once regardless of number of failed validations': function () {
+ var db = start()
+
+ var D = db.model('CallbackFiresOnceValidation', new Schema({
+ username: { type: String, validate: /^[a-z]{6}$/i }
+ , email: { type: String, validate: /^[a-z]{6}$/i }
+ , password: { type: String, validate: /^[a-z]{6}$/i }
+ }));
+
+ var post = new D({
+ username: "nope"
+ , email: "too"
+ , password: "short"
+ });
+
+ var timesCalled = 0;
+
+ post.save(function (err) {
+ db.close();
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ (++timesCalled).should.eql(1);
+
+ (Object.keys(err.errors).length).should.eql(3);
+ err.errors.password.should.be.an.instanceof(ValidatorError);
+ err.errors.email.should.be.an.instanceof(ValidatorError);
+ err.errors.username.should.be.an.instanceof(ValidatorError);
+ err.errors.password.message.should.eql('Validator failed for path password');
+ err.errors.email.message.should.eql('Validator failed for path email');
+ err.errors.username.message.should.eql('Validator failed for path username');
+
+ (Object.keys(post.errors).length).should.eql(3);
+ post.errors.password.should.be.an.instanceof(ValidatorError);
+ post.errors.email.should.be.an.instanceof(ValidatorError);
+ post.errors.username.should.be.an.instanceof(ValidatorError);
+ post.errors.password.message.should.eql('Validator failed for path password');
+ post.errors.email.message.should.eql('Validator failed for path email');
+ post.errors.username.message.should.eql('Validator failed for path username');
+
+ });
+ },
+
+ 'test validation on a query result': function () {
+ mongoose.model('TestValidationOnResult', new Schema({
+ resultv: { type: String, required: true }
+ }));
+
+ var db = start()
+ , TestV = db.model('TestValidationOnResult');
+
+ var post = new TestV;
+
+ post.validate(function (err) {
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ post.resultv = 'yeah';
+ post.save(function (err) {
+ should.strictEqual(err, null);
+ TestV.findOne({ _id: post.id }, function (err, found) {
+ should.strictEqual(err, null);
+ found.resultv.should.eql('yeah');
+ found.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ })
+ });
+ });
+ })
+ },
+
+ 'test required validation for previously existing null values': function () {
+ mongoose.model('TestPreviousNullValidation', new Schema({
+ previous: { type: String, required: true }
+ , a: String
+ }));
+
+ var db = start()
+ , TestP = db.model('TestPreviousNullValidation')
+
+ TestP.collection.insert({ a: null, previous: null}, {}, function (err, f) {
+ should.strictEqual(err, null);
+
+ TestP.findOne({_id: f[0]._id}, function (err, found) {
+ should.strictEqual(err, null);
+ found.isNew.should.be.false;
+ should.strictEqual(found.get('previous'), null);
+
+ found.validate(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ found.set('previous', 'yoyo');
+ found.save(function (err) {
+ should.strictEqual(err, null);
+ db.close();
+ });
+ })
+ })
+ });
+ },
+
+ 'test nested validation': function(){
+ mongoose.model('TestNestedValidation', new Schema({
+ nested: {
+ required: { type: String, required: true }
+ }
+ }));
+
+ var db = start()
+ , TestNestedValidation = db.model('TestNestedValidation');
+
+ var post = new TestNestedValidation();
+ post.set('nested.required', null);
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ post.set('nested.required', 'here');
+ post.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ 'test validation in subdocuments': function(){
+ var Subdocs = new Schema({
+ required: { type: String, required: true }
+ });
+
+ mongoose.model('TestSubdocumentsValidation', new Schema({
+ items: [Subdocs]
+ }));
+
+ var db = start()
+ , TestSubdocumentsValidation = db.model('TestSubdocumentsValidation');
+
+ var post = new TestSubdocumentsValidation();
+
+ post.get('items').push({ required: '' });
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ err.errors.required.should.be.an.instanceof(ValidatorError);
+ err.errors.required.message.should.eql('Validator "required" failed for path required');
+
+ post.get('items')[0].set('required', true);
+ post.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ 'test async validation': function(){
+ var executed = false;
+
+ function validator(v, fn){
+ setTimeout(function () {
+ executed = true;
+ fn(v !== 'test');
+ }, 50);
+ };
+ mongoose.model('TestAsyncValidation', new Schema({
+ async: { type: String, validate: [validator, 'async validator'] }
+ }));
+
+ var db = start()
+ , TestAsyncValidation = db.model('TestAsyncValidation');
+
+ var post = new TestAsyncValidation();
+ post.set('async', 'test');
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ err.errors.async.should.be.an.instanceof(ValidatorError);
+ err.errors.async.message.should.eql('Validator "async validator" failed for path async');
+ executed.should.be.true;
+ executed = false;
+
+ post.set('async', 'woot');
+ post.save(function(err){
+ executed.should.be.true;
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ 'test nested async validation': function(){
+ var executed = false;
+
+ function validator(v, fn){
+ setTimeout(function () {
+ executed = true;
+ fn(v !== 'test');
+ }, 50);
+ };
+
+ mongoose.model('TestNestedAsyncValidation', new Schema({
+ nested: {
+ async: { type: String, validate: [validator, 'async validator'] }
+ }
+ }));
+
+ var db = start()
+ , TestNestedAsyncValidation = db.model('TestNestedAsyncValidation');
+
+ var post = new TestNestedAsyncValidation();
+ post.set('nested.async', 'test');
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ executed.should.be.true;
+ executed = false;
+
+ post.validate(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ executed.should.be.true;
+ executed = false;
+
+ post.set('nested.async', 'woot');
+ post.validate(function(err){
+ executed.should.be.true;
+ should.equal(err, null);
+ executed = false;
+
+ post.save(function(err){
+ executed.should.be.true;
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ })
+
+ });
+ },
+
+ 'test async validation in subdocuments': function(){
+ var executed = false;
+
+ function validator (v, fn) {
+ setTimeout(function(){
+ executed = true;
+ fn(v !== '');
+ }, 50);
+ };
+
+ var Subdocs = new Schema({
+ required: { type: String, validate: [validator, 'async in subdocs'] }
+ });
+
+ mongoose.model('TestSubdocumentsAsyncValidation', new Schema({
+ items: [Subdocs]
+ }));
+
+ var db = start()
+ , Test = db.model('TestSubdocumentsAsyncValidation');
+
+ var post = new Test();
+
+ post.get('items').push({ required: '' });
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ executed.should.be.true;
+ executed = false;
+
+ post.get('items')[0].set({ required: 'here' });
+ post.save(function(err){
+ executed.should.be.true;
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ 'test validation without saving': function(){
+
+ mongoose.model('TestCallingValidation', new Schema({
+ item: { type: String, required: true }
+ }));
+
+ var db = start()
+ , TestCallingValidation = db.model('TestCallingValidation');
+
+ var post = new TestCallingValidation;
+
+ post.schema.path('item').isRequired.should.be.true;
+
+ should.strictEqual(post.isNew, true);
+
+ post.validate(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ should.strictEqual(post.isNew, true);
+
+ post.item = 'yo';
+ post.validate(function(err){
+ should.equal(err, null);
+ should.strictEqual(post.isNew, true);
+ db.close();
+ });
+ });
+
+ },
+
+ 'test setting required to false': function () {
+ function validator () {
+ return true;
+ }
+
+ mongoose.model('TestRequiredFalse', new Schema({
+ result: { type: String, validate: [validator, 'chump validator'], required: false }
+ }));
+
+ var db = start()
+ , TestV = db.model('TestRequiredFalse');
+
+ var post = new TestV;
+
+ post.schema.path('result').isRequired.should.be.false;
+
+ db.close();
+ },
+
+ 'test defaults application': function(){
+ var now = Date.now();
+
+ mongoose.model('TestDefaults', new Schema({
+ date: { type: Date, default: now }
+ }));
+
+ var db = start()
+ , TestDefaults = db.model('TestDefaults');
+
+ var post = new TestDefaults();
+ post.get('date').should.be.an.instanceof(Date);
+ (+post.get('date')).should.eql(now);
+ db.close();
+ },
+
+ 'test "type" is allowed as a key': function(){
+ mongoose.model('TestTypeDefaults', new Schema({
+ type: { type: String, default: 'YES!' }
+ }));
+
+ var db = start()
+ , TestDefaults = db.model('TestTypeDefaults');
+
+ var post = new TestDefaults();
+ post.get('type').should.be.a('string');
+ post.get('type').should.eql('YES!');
+
+ // GH-402
+ var TestDefaults2 = db.model('TestTypeDefaults2', new Schema({
+ x: { y: { type: { type: String }, owner: String } }
+ }));
+
+ var post = new TestDefaults2;
+ post.x.y.type = "#402";
+ post.x.y.owner= "me";
+ post.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+
+ },
+
+ 'test nested defaults application': function(){
+ var now = Date.now();
+
+ mongoose.model('TestNestedDefaults', new Schema({
+ nested: {
+ date: { type: Date, default: now }
+ }
+ }));
+
+ var db = start()
+ , TestDefaults = db.model('TestNestedDefaults');
+
+ var post = new TestDefaults();
+ post.get('nested.date').should.be.an.instanceof(Date);
+ (+post.get('nested.date')).should.eql(now);
+ db.close();
+ },
+
+ 'test defaults application in subdocuments': function(){
+ var now = Date.now();
+
+ var Items = new Schema({
+ date: { type: Date, default: now }
+ });
+
+ mongoose.model('TestSubdocumentsDefaults', new Schema({
+ items: [Items]
+ }));
+
+ var db = start()
+ , TestSubdocumentsDefaults = db.model('TestSubdocumentsDefaults');
+
+ var post = new TestSubdocumentsDefaults();
+ post.get('items').push({});
+ post.get('items')[0].get('date').should.be.an.instanceof(Date);
+ (+post.get('items')[0].get('date')).should.eql(now);
+ db.close();
+ },
+
+ // TODO: adapt this text to handle a getIndexes callback that's not unique to
+ // the mongodb-native driver.
+ 'test that indexes are ensured when the model is compiled': function(){
+ var Indexed = new Schema({
+ name : { type: String, index: true }
+ , last : String
+ , email : String
+ });
+
+ Indexed.index({ last: 1, email: 1 }, { unique: true });
+
+ var db = start()
+ , IndexedModel = db.model('IndexedModel', Indexed, 'indexedmodel' + random())
+ , assertions = 0;
+
+ IndexedModel.on('index', function(){
+ IndexedModel.collection.getIndexes(function(err, indexes){
+ should.strictEqual(err, null);
+
+ for (var i in indexes)
+ indexes[i].forEach(function(index){
+ if (index[0] == 'name')
+ assertions++;
+ if (index[0] == 'last')
+ assertions++;
+ if (index[0] == 'email')
+ assertions++;
+ });
+
+ assertions.should.eql(3);
+ db.close();
+ });
+ });
+ },
+
+ 'test indexes on embedded documents': function () {
+ var BlogPosts = new Schema({
+ _id : { type: ObjectId, index: true }
+ , title : { type: String, index: true }
+ , desc : String
+ });
+
+ var User = new Schema({
+ name : { type: String, index: true }
+ , blogposts : [BlogPosts]
+ });
+
+ var db = start()
+ , UserModel = db.model('DeepIndexedModel', User, 'deepindexedmodel' + random())
+ , assertions = 0;
+
+ UserModel.on('index', function () {
+ UserModel.collection.getIndexes(function (err, indexes) {
+ should.strictEqual(err, null);
+
+ for (var i in indexes)
+ indexes[i].forEach(function(index){
+ if (index[0] == 'name')
+ assertions++;
+ if (index[0] == 'blogposts._id')
+ assertions++;
+ if (index[0] == 'blogposts.title')
+ assertions++;
+ });
+
+ assertions.should.eql(3);
+ db.close();
+ });
+ });
+ },
+
+ 'compound indexes on embedded documents should be created': function () {
+ var BlogPosts = new Schema({
+ title : String
+ , desc : String
+ });
+
+ BlogPosts.index({ title: 1, desc: 1 });
+
+ var User = new Schema({
+ name : { type: String, index: true }
+ , blogposts : [BlogPosts]
+ });
+
+ var db = start()
+ , UserModel = db.model('DeepCompoundIndexModel', User, 'deepcompoundindexmodel' + random())
+ , found = 0;
+
+ UserModel.on('index', function () {
+ UserModel.collection.getIndexes(function (err, indexes) {
+ should.strictEqual(err, null);
+
+ for (var index in indexes) {
+ switch (index) {
+ case 'name_1':
+ case 'blogposts.title_1_blogposts.desc_1':
+ ++found;
+ break;
+ }
+ }
+
+ db.close();
+ found.should.eql(2);
+ });
+ });
+ },
+
+ 'test getters with same name on embedded documents not clashing': function() {
+ var Post = new Schema({
+ title : String
+ , author : { name : String }
+ , subject : { name : String }
+ });
+
+ mongoose.model('PostWithClashGetters', Post);
+
+ var db = start()
+ , PostModel = db.model('PostWithClashGetters', 'postwithclash' + random());
+
+ var post = new PostModel({
+ title: 'Test'
+ , author: { name: 'A' }
+ , subject: { name: 'B' }
+ });
+
+ post.author.name.should.eql('A');
+ post.subject.name.should.eql('B');
+ post.author.name.should.eql('A');
+
+ db.close();
+ },
+
+ 'test post save middleware': function () {
+ var schema = new Schema({
+ title: String
+ });
+
+ var called = 0;
+
+ schema.post('save', function (obj) {
+ obj.title.should.eql('Little Green Running Hood');
+ called.should.equal(0);
+ called++;
+ });
+
+ schema.post('save', function (obj) {
+ obj.title.should.eql('Little Green Running Hood');
+ called.should.equal(1);
+ called++;
+ });
+
+ schema.post('save', function (obj) {
+ obj.title.should.eql('Little Green Running Hood');
+ called.should.equal(2);
+ db.close();
+ });
+
+ var db = start()
+ , TestMiddleware = db.model('TestPostSaveMiddleware', schema);
+
+ var test = new TestMiddleware({ title: 'Little Green Running Hood'});
+
+ test.save(function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test middleware': function () {
+ var schema = new Schema({
+ title: String
+ });
+
+ var called = 0;
+
+ schema.pre('init', function (next) {
+ called++;
+ next();
+ });
+
+ schema.pre('save', function (next) {
+ called++;
+ next(new Error('Error 101'));
+ });
+
+ schema.pre('remove', function (next) {
+ called++;
+ next();
+ });
+
+ mongoose.model('TestMiddleware', schema);
+
+ var db = start()
+ , TestMiddleware = db.model('TestMiddleware');
+
+ var test = new TestMiddleware();
+
+ test.init({
+ title: 'Test'
+ });
+
+ called.should.eql(1);
+
+ test.save(function(err){
+ err.should.be.an.instanceof(Error);
+ err.message.should.eql('Error 101');
+ called.should.eql(2);
+
+ test.remove(function(err){
+ should.strictEqual(err, null);
+ called.should.eql(3);
+ db.close();
+ });
+ });
+ },
+
+ 'test post init middleware': function () {
+ var schema = new Schema({
+ title: String
+ });
+
+ var preinit = 0
+ , postinit = 0
+
+ schema.pre('init', function (next) {
+ ++preinit;
+ next();
+ });
+
+ schema.post('init', function () {
+ ++postinit;
+ });
+
+ mongoose.model('TestPostInitMiddleware', schema);
+
+ var db = start()
+ , Test = db.model('TestPostInitMiddleware');
+
+ var test = new Test({ title: "banana" });
+
+ test.save(function(err){
+ should.strictEqual(err, null);
+
+ Test.findById(test._id, function (err, test) {
+ should.strictEqual(err, null);
+ preinit.should.eql(1);
+ postinit.should.eql(1);
+ test.remove(function(err){
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test update doc casting': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.set('title', '1');
+
+ var id = post.get('_id');
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.update({ title: 1, _id: id }, { title: 2 }, function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findOne({ _id: post.get('_id') }, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.get('title').should.eql('2');
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test $push casting': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.get('numbers').push('3');
+ post.get('numbers')[0].should.equal(3);
+ db.close();
+ },
+
+ 'test $pull casting': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.get('numbers').push(1, 2, 3, 4);
+ post.save( function (err) {
+ BlogPost.findById( post.get('_id'), function (err, found) {
+ found.get('numbers').length.should.equal(4);
+ found.get('numbers').$pull('3');
+ found.save( function (err) {
+ BlogPost.findById( found.get('_id'), function (err, found2) {
+ found2.get('numbers').length.should.equal(3);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test updating numbers atomically': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , totalDocs = 4
+ , saveQueue = [];
+
+ var post = new BlogPost();
+ post.set('meta.visitors', 5);
+
+ post.save(function(err){
+ if (err) throw err;
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('meta.visitors').increment();
+ doc.get('meta.visitors').valueOf().should.be.equal(6);
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('meta.visitors').increment();
+ doc.get('meta.visitors').valueOf().should.be.equal(6);
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('meta.visitors').increment();
+ doc.get('meta.visitors').valueOf().should.be.equal(6);
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('meta.visitors').increment();
+ doc.get('meta.visitors').valueOf().should.be.equal(6);
+ save(doc);
+ });
+
+ function save(doc) {
+ saveQueue.push(doc);
+ if (saveQueue.length == 4)
+ saveQueue.forEach(function (doc) {
+ doc.save(function (err) {
+ if (err) throw err;
+ --totalDocs || complete();
+ });
+ });
+ };
+
+ function complete () {
+ BlogPost.findOne({ _id: post.get('_id') }, function (err, doc) {
+ if (err) throw err;
+ doc.get('meta.visitors').valueOf().should.be.equal(9);
+ db.close();
+ });
+ };
+ });
+ },
+
+ 'test incrementing a number atomically with an arbitrary value': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost');
+
+ var post = new BlogPost();
+
+ post.meta.visitors = 0;
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ post.meta.visitors.increment(50);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ (+doc.meta.visitors).should.eql(50);
+ db.close();
+ });
+ });
+ });
+ },
+
+ // GH-203
+ 'test changing a number non-atomically': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+
+ post.meta.visitors = 5;
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.meta.visitors -= 2;
+
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ (+doc.meta.visitors).should.eql(3);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test saving subdocuments atomically': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , totalDocs = 4
+ , saveQueue = [];
+
+ var post = new BlogPost();
+
+ post.save(function(err){
+ if (err) throw err;
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('comments').push({ title: '1' });
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('comments').push({ title: '2' });
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('comments').push({ title: '3' });
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('comments').push({ title: '4' }, { title: '5' });
+ save(doc);
+ });
+
+ function save(doc) {
+ saveQueue.push(doc);
+ if (saveQueue.length == 4)
+ saveQueue.forEach(function (doc) {
+ doc.save(function (err) {
+ if (err) throw err;
+ --totalDocs || complete();
+ });
+ });
+ };
+
+ function complete () {
+ BlogPost.findOne({ _id: post.get('_id') }, function (err, doc) {
+ if (err) throw err;
+
+ doc.get('comments').length.should.eql(5);
+
+ doc.get('comments').some(function(comment){
+ return comment.get('title') == '1';
+ }).should.be.true;
+
+ doc.get('comments').some(function(comment){
+ return comment.get('title') == '2';
+ }).should.be.true;
+
+ doc.get('comments').some(function(comment){
+ return comment.get('title') == '3';
+ }).should.be.true;
+
+ doc.get('comments').some(function(comment){
+ return comment.get('title') == '4';
+ }).should.be.true;
+
+ doc.get('comments').some(function(comment){
+ return comment.get('title') == '5';
+ }).should.be.true;
+
+ db.close();
+ });
+ };
+ });
+ },
+
+ // GH-310
+ 'test setting a subdocument atomically': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+
+ BlogPost.create({
+ comments: [{ title: 'first-title', body: 'first-body'}]
+ }, function (err, blog) {
+ should.strictEqual(null, err);
+ BlogPost.findById(blog.id, function (err, agent1blog) {
+ should.strictEqual(null, err);
+ BlogPost.findById(blog.id, function (err, agent2blog) {
+ should.strictEqual(null, err);
+ agent1blog.get('comments')[0].title = 'second-title';
+ agent1blog.save( function (err) {
+ should.strictEqual(null, err);
+ agent2blog.get('comments')[0].body = 'second-body';
+ agent2blog.save( function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(blog.id, function (err, foundBlog) {
+ should.strictEqual(null, err);
+ db.close();
+ var comment = foundBlog.get('comments')[0];
+ comment.title.should.eql('second-title');
+ comment.body.should.eql('second-body');
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test doubly nested array saving and loading': function(){
+ var Inner = new Schema({
+ arr: [Number]
+ });
+
+ var Outer = new Schema({
+ inner: [Inner]
+ });
+ mongoose.model('Outer', Outer);
+
+ var db = start();
+ var Outer = db.model('Outer', 'arr_test_' + random());
+
+ var outer = new Outer();
+ outer.inner.push({});
+ outer.save(function(err) {
+ should.strictEqual(err, null);
+ outer.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ Outer.findById(outer.get('_id'), function(err, found) {
+ should.strictEqual(err, null);
+ should.equal(1, found.inner.length);
+ found.inner[0].arr.push(5);
+ found.save(function(err) {
+ should.strictEqual(err, null);
+ found.get('_id').should.be.an.instanceof(DocumentObjectId);
+ Outer.findById(found.get('_id'), function(err, found2) {
+ db.close();
+ should.strictEqual(err, null);
+ should.equal(1, found2.inner.length);
+ should.equal(1, found2.inner[0].arr.length);
+ should.equal(5, found2.inner[0].arr[0]);
+ });
+ });
+ });
+ });
+ },
+
+ 'test updating multiple Number $pushes as a single $pushAll': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({}, function (err, t) {
+ t.nested.nums.push(1);
+ t.nested.nums.push(2);
+
+ t.nested.nums.should.have.length(2);
+
+ t.save( function (err) {
+ should.strictEqual(null, err);
+ t.nested.nums.should.have.length(2);
+ Temp.findById(t._id, function (err, found) {
+ found.nested.nums.should.have.length(2);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test updating at least a single $push and $pushAll as a single $pushAll': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({}, function (err, t) {
+ t.nested.nums.push(1);
+ t.nested.nums.$pushAll([2, 3]);
+
+ t.nested.nums.should.have.length(3);
+
+ t.save( function (err) {
+ should.strictEqual(null, err);
+ t.nested.nums.should.have.length(3);
+ Temp.findById(t._id, function (err, found) {
+ found.nested.nums.should.have.length(3);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test activePaths should be updated for nested modifieds': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({nested: {nums: [1, 2, 3, 4, 5]}}, function (err, t) {
+ t.nested.nums.$pull(1);
+ t.nested.nums.$pull(2);
+
+ t._activePaths.stateOf('nested.nums').should.equal('modify');
+ db.close();
+
+ });
+ },
+
+ '$pull should affect what you see in an array before a save': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({nested: {nums: [1, 2, 3, 4, 5]}}, function (err, t) {
+ t.nested.nums.$pull(1);
+
+ t.nested.nums.should.have.length(4);
+
+ db.close();
+ });
+ },
+
+ '$pullAll should affect what you see in an array before a save': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({nested: {nums: [1, 2, 3, 4, 5]}}, function (err, t) {
+ t.nested.nums.$pullAll([1, 2, 3]);
+
+ t.nested.nums.should.have.length(2);
+
+ db.close();
+ });
+ },
+
+ 'test updating multiple Number $pulls as a single $pullAll': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({nested: {nums: [1, 2, 3, 4, 5]}}, function (err, t) {
+ t.nested.nums.$pull(1);
+ t.nested.nums.$pull(2);
+
+ t.nested.nums.should.have.length(3);
+
+ t.save( function (err) {
+ should.strictEqual(null, err);
+ t.nested.nums.should.have.length(3);
+ Temp.findById(t._id, function (err, found) {
+ found.nested.nums.should.have.length(3);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'having both a pull and pullAll should default to pullAll': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({nested: {nums: [1, 2, 3, 4, 5]}}, function (err, t) {
+ t.nested.nums.$pull(1);
+ t.nested.nums.$pullAll([2, 3]);
+
+ t.nested.nums.should.have.length(2);
+
+ t.save( function (err) {
+ should.strictEqual(null, err);
+ t.nested.nums.should.have.length(2);
+ Temp.findById(t._id, function (err, found) {
+ found.nested.nums.should.have.length(2);
+ db.close();
+ });
+ });
+ });
+ },
+
+ '$shift': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('TestingShift', schema);
+ var Temp = db.model('TestingShift', collection);
+
+ Temp.create({ nested: { nums: [1,2,3] }}, function (err, t) {
+ should.strictEqual(null, err);
+
+ Temp.findById(t._id, function (err, found) {
+ should.strictEqual(null, err);
+ found.nested.nums.should.have.length(3);
+ found.nested.nums.$pop();
+ found.nested.nums.should.have.length(2);
+ found.nested.nums[0].should.eql(1);
+ found.nested.nums[1].should.eql(2);
+
+ found.save(function (err) {
+ should.strictEqual(null, err);
+ Temp.findById(t._id, function (err, found) {
+ should.strictEqual(null, err);
+ found.nested.nums.should.have.length(2);
+ found.nested.nums[0].should.eql(1);
+ found.nested.nums[1].should.eql(2);
+ found.nested.nums.$shift();
+ found.nested.nums.should.have.length(1);
+ found.nested.nums[0].should.eql(2);
+
+ found.save(function (err) {
+ should.strictEqual(null, err);
+ Temp.findById(t._id, function (err, found) {
+ db.close();
+ should.strictEqual(null, err);
+ found.nested.nums.should.have.length(1);
+ found.nested.nums[0].should.eql(2);
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test saving embedded arrays of Numbers atomically': function () {
+ var db = start()
+ , TempSchema = new Schema({
+ nums: [Number]
+ })
+ , totalDocs = 2
+ , saveQueue = [];
+
+ mongoose.model('Temp', TempSchema);
+ var Temp = db.model('Temp', collection);
+
+ var t = new Temp();
+
+ t.save(function(err){
+ if (err) throw err;
+
+ Temp.findOne({ _id: t.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('nums').push(1);
+ save(doc);
+ });
+
+ Temp.findOne({ _id: t.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('nums').push(2, 3);
+ save(doc);
+ });
+
+
+ function save(doc) {
+ saveQueue.push(doc);
+ if (saveQueue.length == totalDocs)
+ saveQueue.forEach(function (doc) {
+ doc.save(function (err) {
+ if (err) throw err;
+ --totalDocs || complete();
+ });
+ });
+ };
+
+ function complete () {
+ Temp.findOne({ _id: t.get('_id') }, function (err, doc) {
+ if (err) throw err;
+
+ doc.get('nums').length.should.eql(3);
+
+ doc.get('nums').some(function(num){
+ return num.valueOf() == '1';
+ }).should.be.true;
+
+ doc.get('nums').some(function(num){
+ return num.valueOf() == '2';
+ }).should.be.true;
+
+ doc.get('nums').some(function(num){
+ return num.valueOf() == '3';
+ }).should.be.true;
+
+
+ db.close();
+ });
+ };
+ });
+ },
+
+ 'test saving embedded arrays of Strings atomically': function () {
+ var db = start()
+ , StrListSchema = new Schema({
+ strings: [String]
+ })
+ , totalDocs = 2
+ , saveQueue = [];
+
+ mongoose.model('StrList', StrListSchema);
+ var StrList = db.model('StrList');
+
+ var t = new StrList();
+
+ t.save(function(err){
+ if (err) throw err;
+
+ StrList.findOne({ _id: t.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('strings').push('a');
+ save(doc);
+ });
+
+ StrList.findOne({ _id: t.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('strings').push('b', 'c');
+ save(doc);
+ });
+
+
+ function save(doc) {
+ saveQueue.push(doc);
+ if (saveQueue.length == totalDocs)
+ saveQueue.forEach(function (doc) {
+ doc.save(function (err) {
+ if (err) throw err;
+ --totalDocs || complete();
+ });
+ });
+ };
+
+ function complete () {
+ StrList.findOne({ _id: t.get('_id') }, function (err, doc) {
+ if (err) throw err;
+
+ doc.get('strings').length.should.eql(3);
+
+ doc.get('strings').some(function(str){
+ return str == 'a';
+ }).should.be.true;
+
+ doc.get('strings').some(function(str){
+ return str == 'b';
+ }).should.be.true;
+
+ doc.get('strings').some(function(str){
+ return str == 'c';
+ }).should.be.true;
+
+ db.close();
+ });
+ };
+ });
+ },
+
+ 'test saving embedded arrays of Buffers atomically': function () {
+ var db = start()
+ , BufListSchema = new Schema({
+ buffers: [Buffer]
+ })
+ , totalDocs = 2
+ , saveQueue = [];
+
+ mongoose.model('BufList', BufListSchema);
+ var BufList = db.model('BufList');
+
+ var t = new BufList();
+
+ t.save(function(err){
+ should.strictEqual(null, err);
+
+ BufList.findOne({ _id: t.get('_id') }, function(err, doc){
+ should.strictEqual(null, err);
+ doc.get('buffers').push(new Buffer([140]));
+ save(doc);
+ });
+
+ BufList.findOne({ _id: t.get('_id') }, function(err, doc){
+ should.strictEqual(null, err);
+ doc.get('buffers').push(new Buffer([141]), new Buffer([142]));
+ save(doc);
+ });
+
+
+ function save(doc) {
+ saveQueue.push(doc);
+ if (saveQueue.length == totalDocs)
+ saveQueue.forEach(function (doc) {
+ doc.save(function (err) {
+ should.strictEqual(null, err);
+ --totalDocs || complete();
+ });
+ });
+ };
+
+ function complete () {
+ BufList.findOne({ _id: t.get('_id') }, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+
+ doc.get('buffers').length.should.eql(3);
+
+ doc.get('buffers').some(function(buf){
+ return buf[0] == 140;
+ }).should.be.true;
+
+ doc.get('buffers').some(function(buf){
+ return buf[0] == 141;
+ }).should.be.true;
+
+ doc.get('buffers').some(function(buf){
+ return buf[0] == 142;
+ }).should.be.true;
+
+ });
+ };
+ });
+ },
+
+ // GH-255
+ 'test updating an embedded document in an embedded array': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({comments: [{title: 'woot'}]}, function (err, post) {
+ should.strictEqual(err, null);
+ BlogPost.findById(post._id, function (err, found) {
+ should.strictEqual(err, null);
+ found.comments[0].title.should.equal('woot');
+ found.comments[0].title = 'notwoot';
+ found.save( function (err) {
+ should.strictEqual(err, null);
+ BlogPost.findById(found._id, function (err, updated) {
+ db.close();
+ should.strictEqual(err, null);
+ updated.comments[0].title.should.equal('notwoot');
+ });
+ });
+ });
+ });
+ },
+
+ // GH-334
+ 'test updating an embedded array document to an Object value': function () {
+ var db = start()
+ , SubSchema = new Schema({
+ name : String ,
+ subObj : { subName : String }
+ });
+ var GH334Schema = new Schema ({ name : String , arrData : [ SubSchema] });
+
+ mongoose.model('GH334' , GH334Schema);
+ var AModel = db.model('GH334');
+ var instance = new AModel();
+
+ instance.set( { name : 'name-value' , arrData : [ { name : 'arrName1' , subObj : { subName : 'subName1' } } ] });
+ instance.save(function(err) {
+ AModel.findById(instance.id, function(err, doc) {
+ doc.arrData[0].set('subObj' , { subName : 'modified subName' });
+ doc.save(function(err) {
+ should.strictEqual(null, err);
+ AModel.findById(instance.id, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.arrData[0].subObj.subName.should.eql('modified subName');
+ });
+ });
+ });
+ });
+ },
+
+ // GH-267
+ 'saving an embedded document twice should not push that doc onto the parent doc twice': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.comments.push({title: 'woot'});
+ post.save( function (err) {
+ should.strictEqual(err, null);
+ post.comments.should.have.length(1);
+ BlogPost.findById(post.id, function (err, found) {
+ should.strictEqual(err, null);
+ found.comments.should.have.length(1);
+ post.save( function (err) {
+ should.strictEqual(err, null);
+ post.comments.should.have.length(1);
+ BlogPost.findById(post.id, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.comments.should.have.length(1);
+ });
+ });
+ });
+ });
+ },
+
+ 'test filtering an embedded array by the id shortcut function': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+
+ post.comments.push({ title: 'woot' });
+ post.comments.push({ title: 'aaaa' });
+
+ var subdoc1 = post.comments[0];
+ var subdoc2 = post.comments[1];
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ // test with an objectid
+ doc.comments.id(subdoc1.get('_id')).title.should.eql('woot');
+
+ // test with a string
+ var id = DocumentObjectId.toString(subdoc2._id);
+ doc.comments.id(id).title.should.eql('aaaa');
+
+ db.close();
+ });
+ });
+ },
+
+ 'test filtering an embedded array by the id with cast error': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+ should.strictEqual(doc.comments.id(null), null);
+
+ db.close();
+ });
+ });
+ },
+
+ 'test filtering an embedded array by the id shortcut with no match': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+ should.strictEqual(doc.comments.id(new DocumentObjectId), null);
+
+ db.close();
+ });
+ });
+ },
+
+ 'test for removing a subdocument atomically': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.title = 'hahaha';
+ post.comments.push({ title: 'woot' });
+ post.comments.push({ title: 'aaaa' });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.comments[0].remove();
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.comments.should.have.length(1);
+ doc.comments[0].title.should.eql('aaaa');
+
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test for single pull embedded doc' : function() {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.title = 'hahaha';
+ post.comments.push({ title: 'woot' });
+ post.comments.push({ title: 'aaaa' });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.comments.pull(doc.comments[0]);
+ doc.comments.pull(doc.comments[0]);
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.comments.should.have.length(0);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'try saving mixed data': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , count = 3;
+
+ // string
+ var post = new BlogPost();
+ post.mixed = 'woot';
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err) {
+ should.strictEqual(err, null);
+
+ --count || db.close();
+ });
+ });
+
+ // array
+ var post2 = new BlogPost();
+ post2.mixed = { name: "mr bungle", arr: [] };
+ post2.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post2._id, function (err, doc){
+ should.strictEqual(err, null);
+
+ Array.isArray(doc.mixed.arr).should.be.true;
+
+ doc.mixed = [{foo: 'bar'}];
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(doc._id, function (err, doc){
+ should.strictEqual(err, null);
+
+ Array.isArray(doc.mixed).should.be.true;
+ doc.mixed.push({ hello: 'world' });
+ doc.mixed.push([ 'foo', 'bar' ]);
+ doc.commit('mixed');
+
+ doc.save(function (err, doc) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post2._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.mixed[0].should.eql({ foo: 'bar' });
+ doc.mixed[1].should.eql({ hello: 'world' });
+ doc.mixed[2].should.eql(['foo','bar']);
+ --count || db.close();
+ });
+ });
+ });
+
+ // date
+ var post3 = new BlogPost();
+ post3.mixed = new Date;
+ post3.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post3._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.mixed.should.be.an.instanceof(Date);
+ --count || db.close();
+ });
+ });
+ });
+
+ });
+ });
+
+ },
+
+ // GH-200
+ 'try populating mixed data from the constructor': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost');
+
+ var post = new BlogPost({
+ mixed: {
+ type: 'test'
+ , github: 'rules'
+ , nested: {
+ number: 3
+ }
+ }
+ });
+
+ post.mixed.type.should.eql('test');
+ post.mixed.github.should.eql('rules');
+ post.mixed.nested.number.should.eql(3);
+
+ db.close();
+ },
+
+ 'test that we instantiate MongooseNumber in arrays': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.numbers.push(1, '2', 3);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ (~doc.numbers.indexOf(1)).should.not.eql(0);
+ (~doc.numbers.indexOf(2)).should.not.eql(0);
+ (~doc.numbers.indexOf(3)).should.not.eql(0);
+
+ db.close();
+ });
+ });
+ },
+
+ 'test removing from an array atomically using MongooseArray#remove': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.numbers.push(1, 2, 3);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.numbers.remove('1');
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.numbers.should.have.length(2);
+ doc.numbers.remove('2', '3');
+
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.numbers.should.have.length(0);
+ db.close();
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test getting a virtual property via get(...)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost({
+ title: 'Letters from Earth'
+ , author: 'Mark Twain'
+ });
+
+ post.get('titleWithAuthor').should.equal('Letters from Earth by Mark Twain');
+
+ db.close();
+ },
+
+ 'test setting a virtual property': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.set('titleWithAuthor', 'Huckleberry Finn by Mark Twain')
+ post.get('title').should.equal('Huckleberry Finn');
+ post.get('author').should.equal('Mark Twain');
+
+ db.close();
+ },
+
+ 'test getting a virtual property via shortcut getter': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost({
+ title: 'Letters from Earth'
+ , author: 'Mark Twain'
+ });
+
+ post.titleWithAuthor.should.equal('Letters from Earth by Mark Twain');
+
+ db.close();
+ },
+
+ // gh-685
+ 'getters should not be triggered at construction': function () {
+ var db = start()
+ , called = false
+
+ db.close();
+
+ var schema = new mongoose.Schema({
+ number: {
+ type:Number
+ , set: function(x){return x/2}
+ , get: function(x){
+ called = true;
+ return x*2;
+ }
+ }
+ });
+
+ var A = mongoose.model('gettersShouldNotBeTriggeredAtConstruction', schema);
+
+ var a = new A({ number: 100 });
+ called.should.be.false;
+ var num = a.number;
+ called.should.be.true;
+ num.valueOf().should.equal(100);
+ a.getValue('number').valueOf().should.equal(50);
+
+ called = false;
+ var b = new A;
+ b.init({ number: 50 });
+ called.should.be.false;
+ num = b.number;
+ called.should.be.true;
+ num.valueOf().should.equal(100);
+ b.getValue('number').valueOf().should.equal(50);
+ },
+
+ 'saving a doc with a set virtual property should persist the real properties but not the virtual property': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.set('titleWithAuthor', 'Huckleberry Finn by Mark Twain')
+ post.get('title').should.equal('Huckleberry Finn');
+ post.get('author').should.equal('Mark Twain');
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, found) {
+ should.strictEqual(err, null);
+
+ found.get('title').should.equal('Huckleberry Finn');
+ found.get('author').should.equal('Mark Twain');
+ found.toObject().should.not.have.property('titleWithAuthor');
+ db.close();
+ });
+ });
+ },
+
+ 'test setting a pseudo-nested virtual property': function () {
+ var db = start()
+ , PersonSchema = new Schema({
+ name: {
+ first: String
+ , last: String
+ }
+ });
+
+ PersonSchema.virtual('name.full')
+ .get( function () {
+ return this.get('name.first') + ' ' + this.get('name.last');
+ })
+ .set( function (fullName) {
+ var split = fullName.split(' ');
+ this.set('name.first', split[0]);
+ this.set('name.last', split[1]);
+ });
+
+ mongoose.model('Person', PersonSchema);
+
+ var Person = db.model('Person')
+ , person = new Person({
+ name: {
+ first: 'Michael'
+ , last: 'Sorrentino'
+ }
+ });
+
+ person.get('name.full').should.equal('Michael Sorrentino');
+ person.set('name.full', 'The Situation');
+ person.get('name.first').should.equal('The');
+ person.get('name.last').should.equal('Situation');
+
+ person.name.full.should.equal('The Situation');
+ person.name.full = 'Michael Sorrentino';
+ person.name.first.should.equal('Michael');
+ person.name.last.should.equal('Sorrentino');
+
+ db.close();
+ },
+
+ 'test removing all documents from a collection via Model.remove': function () {
+ var db = start()
+ , collection = 'blogposts_' + random()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.find({}, function (err, found) {
+ should.strictEqual(err, null);
+
+ found.length.should.equal(1);
+
+ BlogPost.remove({}, function (err) {
+ should.strictEqual(!!err, false);
+
+ BlogPost.find({}, function (err, found2) {
+ should.strictEqual(err, null);
+
+ found2.should.have.length(0);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ // GH-190
+ 'test shorcut getter for a type defined with { type: Native }': function () {
+ var schema = new Schema({
+ date: { type: Date }
+ });
+
+ mongoose.model('ShortcutGetterObject', schema);
+
+ var db = start()
+ , ShortcutGetter = db.model('ShortcutGetterObject', 'shortcut' + random())
+ , post = new ShortcutGetter();
+
+ post.set('date', Date.now());
+ post.date.should.be.an.instanceof(Date);
+
+ db.close();
+ },
+
+ 'test shortcut getter for a nested path': function () {
+ var schema = new Schema({
+ first: {
+ second: [Number]
+ }
+ });
+ mongoose.model('ShortcutGetterNested', schema);
+
+ var db = start()
+ , ShortcutGetterNested = db.model('ShortcutGetterNested', collection)
+ , doc = new ShortcutGetterNested();
+
+ doc.first.should.be.a('object');
+ doc.first.second.should.be.an.instanceof(MongooseArray);
+
+ db.close();
+ },
+
+ // GH-195
+ 'test that save on an unaltered model doesn\'t clear the document': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.title = 'woot';
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ // we deliberately make no alterations
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(doc._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.title.should.eql('woot');
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test that safe mode is the default and it works': function () {
+ var Human = new Schema({
+ name : String
+ , email : { type: String, unique: true }
+ });
+
+ mongoose.model('SafeHuman', Human, true);
+
+ var db = start()
+ , Human = db.model('SafeHuman', 'safehuman' + random());
+
+ var me = new Human({
+ name : 'Guillermo Rauch'
+ , email : 'rauchg@gmail.com'
+ });
+
+ me.save(function (err) {
+ should.strictEqual(err, null);
+
+ Human.findById(me._id, function (err, doc){
+ should.strictEqual(err, null);
+ doc.email.should.eql('rauchg@gmail.com');
+
+ var copycat = new Human({
+ name : 'Lionel Messi'
+ , email : 'rauchg@gmail.com'
+ });
+
+ copycat.save(function (err) {
+ /duplicate/.test(err.message).should.be.true;
+ err.should.be.an.instanceof(Error);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test that safe mode can be turned off': function () {
+ var Human = new Schema({
+ name : String
+ , email : { type: String, unique: true }
+ });
+
+ // turn it off
+ Human.set('safe', false);
+
+ mongoose.model('UnsafeHuman', Human, true);
+
+ var db = start()
+ , Human = db.model('UnsafeHuman', 'unsafehuman' + random());
+
+ var me = new Human({
+ name : 'Guillermo Rauch'
+ , email : 'rauchg@gmail.com'
+ });
+
+ me.save(function (err) {
+ should.strictEqual(err, null);
+
+ Human.findById(me._id, function (err, doc){
+ should.strictEqual(err, null);
+ doc.email.should.eql('rauchg@gmail.com');
+
+ var copycat = new Human({
+ name : 'Lionel Messi'
+ , email : 'rauchg@gmail.com'
+ });
+
+ copycat.save(function (err) {
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'passing null in pre hook works': function () {
+ var db = start();
+ var schema = new Schema({ name: String });
+
+ schema.pre('save', function (next) {
+ next(null); // <<-----
+ });
+
+ var S = db.model('S', schema, collection);
+ var s = new S({name: 'zupa'});
+
+ s.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+
+ },
+
+ 'pre hooks called on all sub levels': function () {
+ var db = start();
+
+ var grandSchema = new Schema({ name : String });
+ grandSchema.pre('save', function (next) {
+ this.name = 'grand';
+ next();
+ });
+
+ var childSchema = new Schema({ name : String, grand : [grandSchema]});
+ childSchema.pre('save', function (next) {
+ this.name = 'child';
+ next();
+ });
+
+ var schema = new Schema({ name: String, child : [childSchema] });
+
+ schema.pre('save', function (next) {
+ this.name = 'parent';
+ next();
+ });
+
+ var S = db.model('presave_hook', schema, 'presave_hook');
+ var s = new S({ name : 'a' , child : [ { name : 'b', grand : [{ name : 'c'}] } ]});
+
+ s.save(function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.name.should.eql('parent');
+ doc.child[0].name.should.eql('child');
+ doc.child[0].grand[0].name.should.eql('grand');
+ });
+ },
+
+ 'pre hooks error on all sub levels': function () {
+ var db = start();
+
+ var grandSchema = new Schema({ name : String });
+ grandSchema.pre('save', function (next) {
+ next(new Error('Error 101'));
+ });
+
+ var childSchema = new Schema({ name : String, grand : [grandSchema]});
+ childSchema.pre('save', function (next) {
+ this.name = 'child';
+ next();
+ });
+
+ var schema = new Schema({ name: String, child : [childSchema] });
+ schema.pre('save', function (next) {
+ this.name = 'parent';
+ next();
+ });
+
+ var S = db.model('presave_hook_error', schema, 'presave_hook_error');
+ var s = new S({ name : 'a' , child : [ { name : 'b', grand : [{ name : 'c'}] } ]});
+
+ s.save(function (err, doc) {
+ db.close();
+ err.should.be.an.instanceof(Error);
+ err.message.should.eql('Error 101');
+ });
+ },
+
+ 'test post hooks': function () {
+ var schema = new Schema({
+ title: String
+ })
+ , save = false
+ , remove = false
+ , init = false
+ , post = undefined;
+
+ schema.post('save', function (arg) {
+ arg.id.should.equal(post.id)
+ save = true;
+ });
+
+ schema.post('init', function () {
+ init = true;
+ });
+
+ schema.post('remove', function (arg) {
+ arg.id.should.eql(post.id)
+ remove = true;
+ });
+
+ mongoose.model('PostHookTest', schema);
+
+ var db = start()
+ , BlogPost = db.model('PostHookTest');
+
+ post = new BlogPost();
+
+ post.save(function (err) {
+ process.nextTick(function () {
+ should.strictEqual(err, null);
+ save.should.be.true;
+ BlogPost.findById(post._id, function (err, doc) {
+ process.nextTick(function () {
+ should.strictEqual(err, null);
+ init.should.be.true;
+
+ doc.remove(function (err) {
+ process.nextTick(function () {
+ should.strictEqual(err, null);
+ remove.should.be.true;
+ db.close();
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test count querying via #run (aka #exec)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable count as promise'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.count({title: 'interoperable count as promise'});
+ query.exec(function (err, count) {
+ should.strictEqual(err, null);
+ count.should.equal(1);
+ db.close();
+ });
+ });
+ },
+
+ 'test update querying via #run (aka #exec)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable update as promise'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.update({title: 'interoperable update as promise'}, {title: 'interoperable update as promise delta'});
+ query.exec(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.count({title: 'interoperable update as promise delta'}, function (err, count) {
+ should.strictEqual(err, null);
+ count.should.equal(1);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test findOne querying via #run (aka #exec)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable findOne as promise'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.findOne({title: 'interoperable findOne as promise'});
+ query.exec(function (err, found) {
+ should.strictEqual(err, null);
+ found.id.should.eql(created.id);
+ db.close();
+ });
+ });
+ },
+
+ 'test find querying via #run (aka #exec)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create(
+ {title: 'interoperable find as promise'}
+ , {title: 'interoperable find as promise'}
+ , function (err, createdOne, createdTwo) {
+ should.strictEqual(err, null);
+ var query = BlogPost.find({title: 'interoperable find as promise'});
+ query.exec(function (err, found) {
+ should.strictEqual(err, null);
+ found.length.should.equal(2);
+ found[0]._id.id.should.eql(createdOne._id.id);
+ found[1]._id.id.should.eql(createdTwo._id.id);
+ db.close();
+ });
+ });
+ },
+
+ 'test remove querying via #run (aka #exec)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create(
+ {title: 'interoperable remove as promise'}
+ , function (err, createdOne, createdTwo) {
+ should.strictEqual(err, null);
+ var query = BlogPost.remove({title: 'interoperable remove as promise'});
+ query.exec(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.count({title: 'interoperable remove as promise'}, function (err, count) {
+ db.close();
+ count.should.equal(0);
+ });
+ });
+ });
+ },
+
+ 'test changing query at the last minute via #run(op, callback)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable ad-hoc as promise'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.count({title: 'interoperable ad-hoc as promise'});
+ query.exec('findOne', function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test count querying via #run (aka #exec) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable count as promise 2'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.count({title: 'interoperable count as promise 2'});
+ var promise = query.exec();
+ promise.addBack(function (err, count) {
+ should.strictEqual(err, null);
+ count.should.equal(1);
+ db.close();
+ });
+ });
+ },
+
+ 'test update querying via #run (aka #exec) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable update as promise 2'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.update({title: 'interoperable update as promise 2'}, {title: 'interoperable update as promise delta 2'});
+ var promise = query.run();
+ promise.addBack(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.count({title: 'interoperable update as promise delta 2'}, function (err, count) {
+ should.strictEqual(err, null);
+ count.should.equal(1);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test findOne querying via #run (aka #exec) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable findOne as promise 2'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.findOne({title: 'interoperable findOne as promise 2'});
+ var promise = query.exec();
+ promise.addBack(function (err, found) {
+ should.strictEqual(err, null);
+ found.id.should.eql(created.id);
+ db.close();
+ });
+ });
+ },
+
+ 'test find querying via #run (aka #exec) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create(
+ {title: 'interoperable find as promise 2'}
+ , {title: 'interoperable find as promise 2'}
+ , function (err, createdOne, createdTwo) {
+ should.strictEqual(err, null);
+ var query = BlogPost.find({title: 'interoperable find as promise 2'});
+ var promise = query.run();
+ promise.addBack(function (err, found) {
+ should.strictEqual(err, null);
+ found.length.should.equal(2);
+ found[0].id;
+ found[1].id;
+ found[0]._id.should.eql(createdOne._id);
+ found[1]._id.should.eql(createdTwo._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test remove querying via #run (aka #exec) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create(
+ {title: 'interoperable remove as promise 2'}
+ , function (err, createdOne, createdTwo) {
+ should.strictEqual(err, null);
+ var query = BlogPost.remove({title: 'interoperable remove as promise 2'});
+ var promise = query.exec();
+ promise.addBack(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.count({title: 'interoperable remove as promise 2'}, function (err, count) {
+ count.should.equal(0);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test changing query at the last minute via #run(op) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable ad-hoc as promise 2'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.count({title: 'interoperable ad-hoc as promise 2'});
+ var promise = query.exec('findOne');
+ promise.addBack(function (err, found) {
+ should.strictEqual(err, null);
+ found._id.id.should.eql(created._id.id);
+ db.close();
+ });
+ });
+ },
+
+ 'test nested obj literal getter/setters': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var date = new Date;
+
+ var meta = {
+ date: date
+ , visitors: 5
+ };
+
+ var post = new BlogPost()
+ post.init({
+ meta: meta
+ });
+
+ post.get('meta').date.should.be.an.instanceof(Date);
+ post.meta.date.should.be.an.instanceof(Date);
+
+ var threw = false;
+ var getter1;
+ var getter2;
+ var strmet;
+ try {
+ strmet = JSON.stringify(meta);
+ getter1 = JSON.stringify(post.get('meta'));
+ getter2 = JSON.stringify(post.meta);
+ } catch (err) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+ getter1 = JSON.parse(getter1);
+ getter2 = JSON.parse(getter2);
+ getter1.visitors.should.eql(getter2.visitors);
+ getter1.date.should.eql(getter2.date);
+
+ post.meta.date = new Date - 1000;
+ post.meta.date.should.be.an.instanceof(Date);
+ post.get('meta').date.should.be.an.instanceof(Date);
+
+ post.meta.visitors = 2;
+ post.get('meta').visitors.should.be.an.instanceof(MongooseNumber);
+ post.meta.visitors.should.be.an.instanceof(MongooseNumber);
+
+ var newmeta = {
+ date: date - 2000
+ , visitors: 234
+ };
+
+ post.set(newmeta, 'meta');
+
+ post.meta.date.should.be.an.instanceof(Date);
+ post.get('meta').date.should.be.an.instanceof(Date);
+ post.meta.visitors.should.be.an.instanceof(MongooseNumber);
+ post.get('meta').visitors.should.be.an.instanceof(MongooseNumber);
+ (+post.meta.date).should.eql(date - 2000);
+ (+post.get('meta').date).should.eql(date - 2000);
+ (+post.meta.visitors).should.eql(234);
+ (+post.get('meta').visitors).should.eql(234);
+
+ // set object directly
+ post.meta = {
+ date: date - 3000
+ , visitors: 4815162342
+ };
+
+ post.meta.date.should.be.an.instanceof(Date);
+ post.get('meta').date.should.be.an.instanceof(Date);
+ post.meta.visitors.should.be.an.instanceof(MongooseNumber);
+ post.get('meta').visitors.should.be.an.instanceof(MongooseNumber);
+ (+post.meta.date).should.eql(date - 3000);
+ (+post.get('meta').date).should.eql(date - 3000);
+ (+post.meta.visitors).should.eql(4815162342);
+ (+post.get('meta').visitors).should.eql(4815162342);
+
+ db.close();
+ },
+
+ 'nested object property access works when root initd with null': function () {
+ var db = start()
+
+ var schema = new Schema({
+ nest: {
+ st: String
+ }
+ });
+
+ mongoose.model('NestedStringA', schema);
+ var T = db.model('NestedStringA', collection);
+
+ var t = new T({ nest: null });
+
+ should.strictEqual(t.nest.st, null);
+ t.nest = { st: "jsconf rules" };
+ t.nest.toObject().should.eql({ st: "jsconf rules" });
+ t.nest.st.should.eql("jsconf rules");
+
+ t.save(function (err) {
+ should.strictEqual(err, null);
+ db.close();
+ })
+
+ },
+
+ 'nested object property access works when root initd with undefined': function () {
+ var db = start()
+
+ var schema = new Schema({
+ nest: {
+ st: String
+ }
+ });
+
+ mongoose.model('NestedStringB', schema);
+ var T = db.model('NestedStringB', collection);
+
+ var t = new T({ nest: undefined });
+
+ should.strictEqual(t.nest.st, undefined);
+ t.nest = { st: "jsconf rules" };
+ t.nest.toObject().should.eql({ st: "jsconf rules" });
+ t.nest.st.should.eql("jsconf rules");
+
+ t.save(function (err) {
+ should.strictEqual(err, null);
+ db.close();
+ })
+ },
+
+ 're-saving object with pre-existing null nested object': function(){
+ var db = start()
+
+ var schema = new Schema({
+ nest: {
+ st: String
+ , yep: String
+ }
+ });
+
+ mongoose.model('NestedStringC', schema);
+ var T = db.model('NestedStringC', collection);
+
+ var t = new T({ nest: null });
+
+ t.save(function (err) {
+ should.strictEqual(err, null);
+
+ t.nest = { st: "jsconf rules", yep: "it does" };
+ t.save(function (err) {
+ should.strictEqual(err, null);
+
+ T.findById(t.id, function (err, t) {
+ should.strictEqual(err, null);
+ t.nest.st.should.eql("jsconf rules");
+ t.nest.yep.should.eql("it does");
+
+ t.nest = null;
+ t.save(function (err) {
+ should.strictEqual(err, null);
+ should.strictEqual(t._doc.nest, null);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'pushing to a nested array of Mixed works on existing doc': function () {
+ var db = start();
+
+ mongoose.model('MySchema', new Schema({
+ nested: {
+ arrays: []
+ }
+ }));
+
+ var DooDad = db.model('MySchema')
+ , doodad = new DooDad({ nested: { arrays: [] } })
+ , date = 1234567890;
+
+ doodad.nested.arrays.push(["+10", "yup", date]);
+
+ doodad.save(function (err) {
+ should.strictEqual(err, null);
+
+ DooDad.findById(doodad._id, function (err, doodad) {
+ should.strictEqual(err, null);
+
+ doodad.nested.arrays.toObject().should.eql([['+10','yup',date]]);
+
+ doodad.nested.arrays.push(["another", 1]);
+
+ doodad.save(function (err) {
+ should.strictEqual(err, null);
+
+ DooDad.findById(doodad._id, function (err, doodad) {
+ should.strictEqual(err, null);
+
+ doodad
+ .nested
+ .arrays
+ .toObject()
+ .should.eql([['+10','yup',date], ["another", 1]]);
+
+ db.close();
+ });
+ });
+ })
+ });
+
+ },
+
+ 'directly setting nested props works when property is named "type"': function () {
+ var db = start();
+
+ function def () {
+ return [{ x: 1 }, { x: 2 }, { x:3 }]
+ }
+
+ mongoose.model('MySchema2', new Schema({
+ nested: {
+ type: { type: String, default: 'yep' }
+ , array: {
+ type: Array, default: def
+ }
+ }
+ }));
+
+ var DooDad = db.model('MySchema2', collection)
+ , doodad = new DooDad()
+
+ doodad.save(function (err) {
+ should.strictEqual(err, null);
+
+ DooDad.findById(doodad._id, function (err, doodad) {
+ should.strictEqual(err, null);
+
+ doodad.nested.type.should.eql("yep");
+ doodad.nested.array.toObject().should.eql([{x:1},{x:2},{x:3}]);
+
+ doodad.nested.type = "nope";
+ doodad.nested.array = ["some", "new", "stuff"];
+
+ doodad.save(function (err) {
+ should.strictEqual(err, null);
+
+ DooDad.findById(doodad._id, function (err, doodad) {
+ should.strictEqual(err, null);
+ db.close();
+
+ doodad.nested.type.should.eql("nope");
+
+ doodad
+ .nested
+ .array
+ .toObject()
+ .should.eql(["some", "new", "stuff"]);
+
+ });
+ });
+ })
+ });
+ },
+
+ 'system.profile should be a default model': function () {
+ var Profile = mongoose.model('system.profile');
+ Profile.schema.paths.ts.should.be.a('object');
+ Profile.schema.paths.info.should.be.a('object');
+ Profile.schema.paths.millis.should.be.a('object');
+
+ Profile.schema.paths.op.should.be.a('object');
+ Profile.schema.paths.ns.should.be.a('object');
+ Profile.schema.paths.query.should.be.a('object');
+ Profile.schema.paths.updateobj.should.be.a('object');
+ Profile.schema.paths.ntoreturn.should.be.a('object');
+ Profile.schema.paths.nreturned.should.be.a('object');
+ Profile.schema.paths.nscanned.should.be.a('object');
+ Profile.schema.paths.responseLength.should.be.a('object');
+ Profile.schema.paths.client.should.be.a('object');
+ Profile.schema.paths.user.should.be.a('object');
+ Profile.schema.paths.idhack.should.be.a('object');
+ Profile.schema.paths.scanAndOrder.should.be.a('object');
+ Profile.schema.paths.keyUpdates.should.be.a('object');
+ should.strictEqual(undefined, Profile.schema.paths._id);
+ should.strictEqual(undefined, Profile.schema.virtuals.id);
+
+ var db = start();
+ Profile = db.model('system.profile');
+ db.close();
+ Profile.schema.paths.ts.should.be.a('object');
+ Profile.schema.paths.info.should.be.a('object');
+ Profile.schema.paths.millis.should.be.a('object');
+ Profile.schema.paths.op.should.be.a('object');
+ Profile.schema.paths.ns.should.be.a('object');
+ Profile.schema.paths.query.should.be.a('object');
+ Profile.schema.paths.updateobj.should.be.a('object');
+ Profile.schema.paths.ntoreturn.should.be.a('object');
+ Profile.schema.paths.nreturned.should.be.a('object');
+ Profile.schema.paths.nscanned.should.be.a('object');
+ Profile.schema.paths.responseLength.should.be.a('object');
+ Profile.schema.paths.client.should.be.a('object');
+ Profile.schema.paths.user.should.be.a('object');
+ Profile.schema.paths.idhack.should.be.a('object');
+ Profile.schema.paths.scanAndOrder.should.be.a('object');
+ Profile.schema.paths.keyUpdates.should.be.a('object');
+ should.strictEqual(undefined, Profile.schema.paths._id);
+ should.strictEqual(undefined, Profile.schema.virtuals.id);
+
+ // can override the default
+ db = start();
+ // reset Mongoose state
+ delete db.base.modelSchemas['system.profile']
+ delete db.base.models['system.profile']
+ delete db.models['system.profile'];
+ db.close();
+ // test
+ var over = db.model('system.profile', new Schema({ name: String }));
+ over.schema.paths.name.should.be.a('object');
+ should.strictEqual(undefined, over.schema.paths.ts);
+ // reset
+ delete db.base.modelSchemas['system.profile']
+ delete db.base.models['system.profile']
+ delete db.models['system.profile'];
+ },
+
+ 'setting profiling levels': function () {
+ var db = start();
+ db.setProfiling(3, function (err) {
+ err.message.should.eql('Invalid profiling level: 3');
+ db.setProfiling('fail', function (err) {
+ err.message.should.eql('Invalid profiling level: fail');
+ db.setProfiling(2, function (err, doc) {
+ should.strictEqual(err, null);
+ db.setProfiling(1, 50, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.was.should.eql(2);
+ db.setProfiling(0, function (err, doc) {
+ db.close();
+ should.strictEqual(err, null);
+ doc.was.should.eql(1);
+ doc.slowms.should.eql(50);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test post hooks on embedded documents': function(){
+ var save = false,
+ init = false,
+ remove = false;
+
+ var EmbeddedSchema = new Schema({
+ title : String
+ });
+
+ var ParentSchema = new Schema({
+ embeds : [EmbeddedSchema]
+ });
+
+ EmbeddedSchema.post('save', function(next){
+ save = true;
+ });
+
+ // Don't know how to test those on a embedded document.
+ //EmbeddedSchema.post('init', function () {
+ //init = true;
+ //});
+
+ //EmbeddedSchema.post('remove', function () {
+ //remove = true;
+ //});
+
+ mongoose.model('Parent', ParentSchema);
+
+ var db = start(),
+ Parent = db.model('Parent');
+
+ var parent = new Parent();
+
+ parent.embeds.push({title: 'Testing post hooks for embedded docs'});
+
+ parent.save(function(err){
+ db.close();
+ should.strictEqual(err, null);
+ save.should.be.true;
+ });
+ },
+
+ 'console.log shows helpful values': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var date = new Date(1305730951086);
+ var id0 = new DocumentObjectId('4dd3e169dbfb13b4570000b9');
+ var id1 = new DocumentObjectId('4dd3e169dbfb13b4570000b6');
+ var id2 = new DocumentObjectId('4dd3e169dbfb13b4570000b7');
+ var id3 = new DocumentObjectId('4dd3e169dbfb13b4570000b8');
+
+ var post = new BlogPost({
+ title: 'Test'
+ , _id: id0
+ , date: date
+ , numbers: [5,6,7]
+ , owners: [id1]
+ , meta: { visitors: 45 }
+ , comments: [
+ { _id: id2, title: 'my comment', date: date, body: 'this is a comment' },
+ { _id: id3, title: 'the next thang', date: date, body: 'this is a comment too!' }]
+ });
+
+ db.close();
+
+ var a = '{ meta: { visitors: 45 },\n numbers: [ 5, 6, 7 ],\n owners: [ 4dd3e169dbfb13b4570000b6 ],\n comments: \n [{ _id: 4dd3e169dbfb13b4570000b7,\n comments: [],\n body: \'this is a comment\',\n date: Wed, 18 May 2011 15:02:31 GMT,\n title: \'my comment\' }\n { _id: 4dd3e169dbfb13b4570000b8,\n comments: [],\n body: \'this is a comment too!\',\n date: Wed, 18 May 2011 15:02:31 GMT,\n title: \'the next thang\' }],\n _id: 4dd3e169dbfb13b4570000b9,\n date: Wed, 18 May 2011 15:02:31 GMT,\n title: \'Test\' }'
+
+ var out = post.inspect();
+ ;/meta: { visitors: 45 }/.test(out).should.be.true;
+ ;/numbers: \[ 5, 6, 7 \]/.test(out).should.be.true;
+ ;/date: Wed, 18 May 2011 15:02:31 GMT/.test(out).should.be.true;
+ ;/activePaths:/.test(out).should.be.false;
+ ;/_atomics:/.test(out).should.be.false;
+ },
+
+ 'path can be used as pathname': function () {
+ var db = start()
+ , P = db.model('pathnametest', new Schema({ path: String }))
+ db.close();
+
+ var threw = false;
+ try {
+ new P({ path: 'i should not throw' });
+ } catch (err) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+ },
+
+ 'when mongo is down, save callback should fire with err if auto_reconnect is disabled': function () {
+ var db = start({ server: { auto_reconnect: false }});
+ var T = db.model('Thing', new Schema({ type: String }));
+ db.on('open', function () {
+ var t = new T({ type: "monster" });
+
+ var worked = false;
+ t.save(function (err) {
+ worked = true;
+ err.message.should.eql('no open connections');
+ });
+
+ process.nextTick(function () {
+ db.close();
+ });
+
+ setTimeout(function () {
+ worked.should.be.true;
+ }, 500);
+ });
+ },
+
+ //'when mongo is down, auto_reconnect should kick in and db operation should succeed': function () {
+ //var db = start();
+ //var T = db.model('Thing', new Schema({ type: String }));
+ //db.on('open', function () {
+ //var t = new T({ type: "monster" });
+
+ //var worked = false;
+ //t.save(function (err) {
+ //should.strictEqual(err, null);
+ //worked = true;
+ //});
+
+ //process.nextTick(function () {
+ //db.close();
+ //});
+
+ //setTimeout(function () {
+ //worked.should.be.true;
+ //}, 500);
+ //});
+ //},
+
+ 'subdocuments with changed values should persist the values': function () {
+ var db = start()
+ var Subdoc = new Schema({ name: String, mixed: Schema.Types.Mixed });
+ var T = db.model('SubDocMixed', new Schema({ subs: [Subdoc] }));
+
+ var t = new T({ subs: [{ name: "Hubot", mixed: { w: 1, x: 2 }}] });
+ t.subs[0].name.should.equal("Hubot");
+ t.subs[0].mixed.w.should.equal(1);
+ t.subs[0].mixed.x.should.equal(2);
+
+ t.save(function (err) {
+ should.strictEqual(null, err);
+
+ T.findById(t._id, function (err, t) {
+ should.strictEqual(null, err);
+ t.subs[0].name.should.equal("Hubot");
+ t.subs[0].mixed.w.should.equal(1);
+ t.subs[0].mixed.x.should.equal(2);
+
+ var sub = t.subs[0];
+ sub.name = "Hubot1";
+ sub.name.should.equal("Hubot1");
+ sub.isModified('name').should.be.true;
+ t.modified.should.be.true;
+
+ t.save(function (err) {
+ should.strictEqual(null, err);
+
+ T.findById(t._id, function (err, t) {
+ should.strictEqual(null, err);
+ should.strictEqual(t.subs[0].name, "Hubot1");
+
+ var sub = t.subs[0];
+ sub.mixed.w = 5;
+ sub.mixed.w.should.equal(5);
+ sub.isModified('mixed').should.be.false;
+ sub.commit('mixed');
+ sub.isModified('mixed').should.be.true;
+ sub.modified.should.be.true;
+ t.modified.should.be.true;
+
+ t.save(function (err) {
+ should.strictEqual(null, err);
+
+ T.findById(t._id, function (err, t) {
+ db.close();
+ should.strictEqual(null, err);
+ should.strictEqual(t.subs[0].mixed.w, 5);
+ })
+ })
+ });
+ });
+ })
+ })
+ },
+
+ 'RegExps can be saved': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({ mixed: { rgx: /^asdf$/ } });
+ post.mixed.rgx.should.be.instanceof(RegExp);
+ post.mixed.rgx.source.should.eql('^asdf$');
+ post.save(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.findById(post._id, function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+ post.mixed.rgx.should.be.instanceof(RegExp);
+ post.mixed.rgx.source.should.eql('^asdf$');
+ });
+ });
+ },
+
+ 'null defaults are allowed': function () {
+ var db = start();
+ var T = db.model('NullDefault', new Schema({ name: { type: String, default: null }}), collection);
+ var t = new T();
+
+ should.strictEqual(null, t.name);
+
+ t.save(function (err) {
+ should.strictEqual(null, err);
+
+ T.findById(t._id, function (err, t) {
+ db.close();
+ should.strictEqual(null, err);
+ should.strictEqual(null, t.name);
+ });
+ });
+ },
+
+ // GH-365, GH-390, GH-422
+ 'test that setters are used on embedded documents': function () {
+ var db = start();
+
+ function setLat (val) {
+ return parseInt(val);
+ }
+
+ var tick = 0;
+ function uptick () {
+ return ++tick;
+ }
+
+ var Location = new Schema({
+ lat: { type: Number, default: 0, set: setLat}
+ , long: { type: Number, set: uptick }
+ });
+
+ var Deal = new Schema({
+ title: String
+ , locations: [Location]
+ });
+
+ Location = db.model('Location', Location, 'locations_' + random());
+ Deal = db.model('Deal', Deal, 'deals_' + random());
+
+ var location = new Location({lat: 1.2, long: 10});
+ location.lat.valueOf().should.equal(1);
+ location.long.valueOf().should.equal(1);
+
+ var deal = new Deal({title: "My deal", locations: [{lat: 1.2, long: 33}]});
+ deal.locations[0].lat.valueOf().should.equal(1);
+ deal.locations[0].long.valueOf().should.equal(2);
+
+ deal.save(function (err) {
+ should.strictEqual(err, null);
+ Deal.findById(deal._id, function (err, deal) {
+ db.close();
+ should.strictEqual(err, null);
+ deal.locations[0].lat.valueOf().should.equal(1);
+ // GH-422
+ deal.locations[0].long.valueOf().should.equal(2);
+ });
+ });
+ },
+
+ // GH-289
+ 'test that pre-init middleware has access to the true ObjectId when used with querying': function () {
+ var db = start()
+ , PreInitSchema = new Schema({})
+ , preId;
+ PreInitSchema.pre('init', function (next) {
+ preId = this._id;
+ next();
+ });
+ var PreInit = db.model('PreInit', PreInitSchema, 'pre_inits' + random());
+
+ var doc = new PreInit();
+ doc.save( function (err) {
+ should.strictEqual(err, null);
+ PreInit.findById(doc._id, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(undefined, preId);
+ });
+ });
+ },
+
+ // Demonstration showing why GH-261 is a misunderstanding
+ 'a single instantiated document should be able to update its embedded documents more than once': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.comments.push({title: 'one'});
+ post.save(function (err) {
+ should.strictEqual(err, null);
+ post.comments[0].title.should.eql('one');
+ post.comments[0].title = 'two';
+ post.comments[0].title.should.eql('two');
+ post.save( function (err) {
+ should.strictEqual(err, null);
+ BlogPost.findById(post._id, function (err, found) {
+ should.strictEqual(err, null);
+ db.close();
+ should.strictEqual(err, null);
+ found.comments[0].title.should.eql('two');
+ });
+ });
+ });
+ },
+
+ 'defining a method on a Schema corresponding to an EmbeddedDocument should generate an instance method for the ED': function () {
+ var db = start();
+ var ChildSchema = new Schema({ name: String });
+ ChildSchema.method('talk', function () {
+ return 'gaga';
+ });
+
+ var ParentSchema = new Schema({
+ children: [ChildSchema]
+ });
+
+ var ChildA = db.model('ChildA', ChildSchema, 'children_' + random());
+ var ParentA = db.model('ParentA', ParentSchema, 'parents_' + random());
+
+ var c = new ChildA;
+ c.talk.should.be.a('function');
+
+ var p = new ParentA();
+ p.children.push({});
+ p.children[0].talk.should.be.a('function');
+ db.close();
+ },
+
+ 'Model#save should throw errors if a callback is not passed': function () {
+ var db = start();
+
+ var DefaultErrSchema = new Schema({});
+ DefaultErrSchema.pre('save', function (next) {
+ try {
+ next(new Error);
+ } catch (error) {
+ // throws b/c nothing is listening to the error event
+ db.close();
+ error.should.be.instanceof(Error);
+ }
+ });
+ var DefaultErr = db.model('DefaultErr1', DefaultErrSchema, 'default_err_' + random());
+ new DefaultErr().save();
+ },
+
+ 'Model#save should emit an error on its db if a callback is not passed to it': function () {
+ var db = start();
+
+ db.on('error', function (err) {
+ db.close();
+ err.should.be.an.instanceof(Error);
+ });
+
+ var DefaultErrSchema = new Schema({});
+ DefaultErrSchema.pre('save', function (next) {
+ next(new Error);
+ });
+ var DefaultErr = db.model('DefaultErr2', DefaultErrSchema, 'default_err_' + random());
+ new DefaultErr().save();
+ },
+
+ // once hooks-js merges our fix this will pass
+ 'calling next() after a thrown error should not work': function () {
+ var db = start();
+
+ var s = new Schema({});
+ s.methods.funky = function () {
+ should.strictEqual(false, true, 'reached unreachable code');
+ }
+
+ s.pre('funky', function (next) {
+ db.close();
+ try {
+ next(new Error);
+ } catch (error) {
+ error.should.be.instanceof(Error);
+ next();
+ // throws b/c nothing is listening to the error event
+ }
+ });
+ var Kaboom = db.model('wowNext2xAndThrow', s, 'next2xAndThrow' + random());
+ new Kaboom().funky();
+ },
+
+ 'ensureIndex error should emit on the db': function () {
+ var db = start();
+
+ db.on('error', function (err) {
+ /^E11000 duplicate key error index:/.test(err.message).should.equal(true);
+ db.close();
+ });
+
+ var schema = new Schema({ name: { type: String } })
+ , Test = db.model('IndexError', schema, "x"+random());
+
+ Test.create({ name: 'hi' }, { name: 'hi' }, function (err) {
+ should.strictEqual(err, null);
+ Test.schema.index({ name: 1 }, { unique: true });
+ Test.init();
+ });
+ },
+
+ 'backward compatibility with conflicted data in the db': function () {
+ var db = start();
+ var M = db.model('backwardDataConflict', new Schema({ namey: { first: String, last: String }}));
+ var m = new M({ namey: "[object Object]" });
+ m.namey = { first: 'GI', last: 'Joe' };// <-- should overwrite the string
+ m.save(function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual('GI', m.namey.first);
+ should.strictEqual('Joe', m.namey.last);
+ });
+ },
+
+ '#push should work on EmbeddedDocuments more than 2 levels deep': function () {
+ var db = start()
+ , Post = db.model('BlogPost', collection)
+ , Comment = db.model('CommentNesting', Comments, collection);
+
+ var p =new Post({ title: "comment nesting" });
+ var c1 =new Comment({ title: "c1" });
+ var c2 =new Comment({ title: "c2" });
+ var c3 =new Comment({ title: "c3" });
+
+ p.comments.push(c1);
+ c1.comments.push(c2);
+ c2.comments.push(c3);
+
+ p.save(function (err) {
+ should.strictEqual(err, null);
+
+ Post.findById(p._id, function (err, p) {
+ should.strictEqual(err, null);
+
+ var c4=new Comment({ title: "c4" });
+ p.comments[0].comments[0].comments[0].comments.push(c4);
+ p.save(function (err) {
+ should.strictEqual(err, null);
+
+ Post.findById(p._id, function (err, p) {
+ db.close();
+ should.strictEqual(err, null);
+ p.comments[0].comments[0].comments[0].comments[0].title.should.equal('c4');
+ });
+ });
+ });
+ })
+
+ },
+
+ 'test standalone invalidate': function() {
+ var db = start()
+ , InvalidateSchema = null
+ , Post = null
+ , post = null;
+
+ InvalidateSchema = new Schema({
+ prop: { type: String },
+ });
+
+ mongoose.model('InvalidateSchema', InvalidateSchema);
+
+ Post = db.model('InvalidateSchema');
+ post = new Post();
+ post.set({baz: 'val'});
+ post.invalidate('baz', 'reason');
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ err.errors.baz.should.be.an.instanceof(ValidatorError);
+ err.errors.baz.message.should.equal('Validator "reason" failed for path baz');
+ err.errors.baz.type.should.equal('reason');
+ err.errors.baz.path.should.equal('baz');
+
+ post.save(function(err){
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ },
+
+ 'test simple validation middleware': function() {
+ var db = start()
+ , ValidationMiddlewareSchema = null
+ , Post = null
+ , post = null;
+
+ ValidationMiddlewareSchema = new Schema({
+ baz: { type: String }
+ });
+
+ ValidationMiddlewareSchema.pre('validate', function(next) {
+ if (this.get('baz') == 'bad') {
+ this.invalidate('baz', 'bad');
+ }
+ next();
+ });
+
+ mongoose.model('ValidationMiddleware', ValidationMiddlewareSchema);
+
+ Post = db.model('ValidationMiddleware');
+ post = new Post();
+ post.set({baz: 'bad'});
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ err.errors.baz.type.should.equal('bad');
+ err.errors.baz.path.should.equal('baz');
+
+ post.set('baz', 'good');
+ post.save(function(err){
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ },
+
+ 'test async validation middleware': function() {
+ var db = start()
+ , AsyncValidationMiddlewareSchema = null
+ , Post = null
+ , post = null;
+
+ AsyncValidationMiddlewareSchema = new Schema({
+ prop: { type: String }
+ });
+
+ AsyncValidationMiddlewareSchema.pre('validate', true, function(next, done) {
+ var self = this;
+ setTimeout(function() {
+ if (self.get('prop') == 'bad') {
+ self.invalidate('prop', 'bad');
+ }
+ done();
+ }, 50);
+ next();
+ });
+
+ mongoose.model('AsyncValidationMiddleware', AsyncValidationMiddlewareSchema);
+
+ Post = db.model('AsyncValidationMiddleware');
+ post = new Post();
+ post.set({prop: 'bad'});
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ err.errors.prop.type.should.equal('bad');
+ err.errors.prop.path.should.equal('prop');
+
+ post.set('prop', 'good');
+ post.save(function(err){
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ },
+
+ 'test complex validation middleware': function() {
+ var db = start()
+ , ComplexValidationMiddlewareSchema = null
+ , Post = null
+ , post = null
+ , abc = function(v) {
+ return v === 'abc';
+ };
+
+ ComplexValidationMiddlewareSchema = new Schema({
+ baz: { type: String },
+ abc: { type: String, validate: [abc, 'must be abc'] },
+ test: { type: String, validate: [/test/, 'must also be abc'] },
+ required: { type: String, required: true }
+ });
+
+ ComplexValidationMiddlewareSchema.pre('validate', true, function(next, done) {
+ var self = this;
+ setTimeout(function() {
+ if (self.get('baz') == 'bad') {
+ self.invalidate('baz', 'bad');
+ }
+ done();
+ }, 50);
+ next();
+ });
+
+ mongoose.model('ComplexValidationMiddleware', ComplexValidationMiddlewareSchema);
+
+ Post = db.model('ComplexValidationMiddleware');
+ post = new Post();
+ post.set({
+ baz: 'bad',
+ abc: 'not abc',
+ test: 'fail'
+ });
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ Object.keys(err.errors).length.should.equal(4);
+ err.errors.baz.should.be.an.instanceof(ValidatorError);
+ err.errors.baz.type.should.equal('bad');
+ err.errors.baz.path.should.equal('baz');
+ err.errors.abc.should.be.an.instanceof(ValidatorError);
+ err.errors.abc.type.should.equal('must be abc');
+ err.errors.abc.path.should.equal('abc');
+ err.errors.test.should.be.an.instanceof(ValidatorError);
+ err.errors.test.type.should.equal('must also be abc');
+ err.errors.test.path.should.equal('test');
+ err.errors.required.should.be.an.instanceof(ValidatorError);
+ err.errors.required.type.should.equal('required');
+ err.errors.required.path.should.equal('required');
+
+ post.set({
+ baz: 'good',
+ abc: 'abc',
+ test: 'test',
+ required: 'here'
+ });
+
+ post.save(function(err){
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ },
+
+ 'Model.create can accept an array': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create([{ title: 'hi'}, { title: 'bye'}], function (err, post1, post2) {
+ db.close();
+ should.strictEqual(err, null);
+ post1.get('_id').should.be.an.instanceof(DocumentObjectId);
+ post2.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ post1.title.should.equal('hi');
+ post2.title.should.equal('bye');
+ });
+ },
+
+ 'Model.create when passed no docs still fires callback': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create(function (err, a) {
+ db.close();
+ should.strictEqual(err, null);
+ should.not.exist(a);
+ });
+ },
+
+ 'Model.create fires callback when an empty array is passed': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create([], function (err, a) {
+ db.close();
+ should.strictEqual(err, null);
+ should.not.exist(a);
+ });
+ },
+
+ 'enhanced date casting test (datejs - gh-502)': function () {
+ var db = start()
+
+ Date.prototype.toObject = function() {
+ return {
+ millisecond: 86
+ , second: 42
+ , minute: 47
+ , hour: 17
+ , day: 13
+ , week: 50
+ , month: 11
+ , year: 2011
+ };
+ };
+
+ var S = new Schema({
+ name: String
+ , description: String
+ , sabreId: String
+ , data: {
+ lastPrice: Number
+ , comm: String
+ , curr: String
+ , rateName: String
+ }
+ , created: { type: Date, default: Date.now }
+ , valid: { type: Boolean, default: true }
+ });
+
+ var M = db.model('gh502', S);
+
+ var m = new M;
+ m.save(function (err) {
+ should.strictEqual(err, null);
+ M.findById(m._id, function (err, m) {
+ should.strictEqual(err, null);
+ m.save(function (err) {
+ should.strictEqual(err, null);
+ M.remove(function (err) {
+ delete Date.prototype.toObject;
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'should not persist non-schema props': function () {
+ var db = start()
+ , B = db.model('BlogPost', collection)
+
+ var b = new B;
+ b.whateveriwant = 10;
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ B.collection.findOne({ _id: b._id }, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ ;('whateveriwant' in doc).should.be.false;
+ });
+ });
+ },
+
+ 'should not throw range error when using Number _id and saving existing doc': function () {
+ var db =start();
+
+ var T = new Schema({ _id: Number, a: String });
+
+ var D = db.model('Testing691', T, 'asdf' + random());
+
+ var d = new D({ _id: 1 });
+ d.save(function (err) {
+ should.strictEqual(null, err);
+
+ D.findById(d._id, function (err, d) {
+ should.strictEqual(null, err);
+
+ d.a = 'yo';
+ d.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ },
+
+ 'number of affected docs should be returned when saving': function () {
+ var db = start()
+ var schema = new Schema({ name: String });
+ var S = db.model('AffectedDocsAreReturned', schema);
+ var s = new S({ name: 'aaron' });
+ s.save(function (err, doc, affected) {
+ should.strictEqual(null, err);
+ affected.should.equal(1);
+ s.name = 'heckmanananananana';
+ s.save(function (err, doc, affected) {
+ db.close();
+ should.strictEqual(null, err);
+ affected.should.equal(1);
+ });
+ });
+ }
+};
diff --git a/node_modules/mongoose/test/model.update.test.js b/node_modules/mongoose/test/model.update.test.js
new file mode 100644
index 0000000..8e795d5
--- /dev/null
+++ b/node_modules/mongoose/test/model.update.test.js
@@ -0,0 +1,526 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Query = require('../lib/query')
+ , Schema = mongoose.Schema
+ , SchemaType = mongoose.SchemaType
+ , CastError = SchemaType.CastError
+ , ValidatorError = SchemaType.ValidatorError
+ , ValidationError = mongoose.Document.ValidationError
+ , ObjectId = Schema.ObjectId
+ , DocumentObjectId = mongoose.Types.ObjectId
+ , DocumentArray = mongoose.Types.DocumentArray
+ , EmbeddedDocument = mongoose.Types.Embedded
+ , MongooseNumber = mongoose.Types.Number
+ , MongooseArray = mongoose.Types.Array
+ , MongooseError = mongoose.Error;
+
+/**
+ * Setup.
+ */
+
+var Comments = new Schema();
+
+Comments.add({
+ title : String
+ , date : Date
+ , body : String
+ , comments : [Comments]
+});
+
+var BlogPost = new Schema({
+ title : String
+ , author : String
+ , slug : String
+ , date : Date
+ , meta : {
+ date : Date
+ , visitors : Number
+ }
+ , published : Boolean
+ , mixed : {}
+ , numbers : [Number]
+ , owners : [ObjectId]
+ , comments : [Comments]
+});
+
+BlogPost.virtual('titleWithAuthor')
+ .get(function () {
+ return this.get('title') + ' by ' + this.get('author');
+ })
+ .set(function (val) {
+ var split = val.split(' by ');
+ this.set('title', split[0]);
+ this.set('author', split[1]);
+ });
+
+BlogPost.method('cool', function(){
+ return this;
+});
+
+BlogPost.static('woot', function(){
+ return this;
+});
+
+mongoose.model('BlogPost', BlogPost);
+
+var collection = 'blogposts_' + random();
+
+var strictSchema = new Schema({ name: String }, { strict: true });
+mongoose.model('UpdateStrictSchema', strictSchema);
+
+module.exports = {
+
+ 'test updating documents': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , title = 'Tobi ' + random()
+ , author = 'Brian ' + random()
+ , newTitle = 'Woot ' + random()
+ , id0 = new DocumentObjectId
+ , id1 = new DocumentObjectId
+
+ var post = new BlogPost();
+ post.set('title', title);
+ post.author = author;
+ post.meta.visitors = 0;
+ post.date = new Date;
+ post.published = true;
+ post.mixed = { x: 'ex' };
+ post.numbers = [4,5,6,7];
+ post.owners = [id0, id1];
+ post.comments = [{ body: 'been there' }, { body: 'done that' }];
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.findById(post._id, function (err, cf) {
+ should.strictEqual(err, null);
+ cf.title.should.equal(title);
+ cf.author.should.equal(author);
+ cf.meta.visitors.valueOf().should.eql(0);
+ cf.date.should.eql(post.date);
+ cf.published.should.be.true;
+ cf.mixed.x.should.equal('ex');
+ cf.numbers.toObject().should.eql([4,5,6,7]);
+ cf.owners.length.should.equal(2);
+ cf.owners[0].toString().should.equal(id0.toString());
+ cf.owners[1].toString().should.equal(id1.toString());
+ cf.comments.length.should.equal(2);
+ cf.comments[0].body.should.eql('been there');
+ cf.comments[1].body.should.eql('done that');
+ should.exist(cf.comments[0]._id);
+ should.exist(cf.comments[1]._id);
+ cf.comments[0]._id.should.be.an.instanceof(DocumentObjectId)
+ cf.comments[1]._id.should.be.an.instanceof(DocumentObjectId);
+
+ var update = {
+ title: newTitle // becomes $set
+ , $inc: { 'meta.visitors': 2 }
+ , $set: { date: new Date }
+ , published: false // becomes $set
+ , 'mixed': { x: 'ECKS', y: 'why' } // $set
+ , $pullAll: { 'numbers': [4, 6] }
+ , $pull: { 'owners': id0 }
+ , 'comments.1.body': 8 // $set
+ }
+
+ BlogPost.update({ title: title }, update, function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, up) {
+ should.strictEqual(err, null);
+ up.title.should.equal(newTitle);
+ up.author.should.equal(author);
+ up.meta.visitors.valueOf().should.equal(2);
+ up.date.toString().should.equal(update.$set.date.toString());
+ up.published.should.eql(false);
+ up.mixed.x.should.equal('ECKS');
+ up.mixed.y.should.equal('why');
+ up.numbers.toObject().should.eql([5,7]);
+ up.owners.length.should.equal(1);
+ up.owners[0].toString().should.eql(id1.toString());
+ up.comments[0].body.should.equal('been there');
+ up.comments[1].body.should.equal('8');
+ should.exist(up.comments[0]._id);
+ should.exist(up.comments[1]._id);
+ up.comments[0]._id.should.be.an.instanceof(DocumentObjectId)
+ up.comments[1]._id.should.be.an.instanceof(DocumentObjectId);
+
+ var update2 = {
+ 'comments.body': 'fail'
+ }
+
+ BlogPost.update({ _id: post._id }, update2, function (err) {
+ should.strictEqual(!!err, true);
+ ;/^can't append to array using string field name \[body\]/.test(err.message).should.be.true;
+ BlogPost.findById(post, function (err, p) {
+ should.strictEqual(null, err);
+
+ var update3 = {
+ $pull: 'fail'
+ }
+
+ BlogPost.update({ _id: post._id }, update3, function (err) {
+ should.strictEqual(!!err, true);
+ ;/Invalid atomic update value/.test(err.message).should.be.true;
+
+ var update4 = {
+ $inc: { idontexist: 1 }
+ }
+
+ // should not overwrite doc when no valid paths are submitted
+ BlogPost.update({ _id: post._id }, update4, function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, up) {
+ should.strictEqual(err, null);
+
+ up.title.should.equal(newTitle);
+ up.author.should.equal(author);
+ up.meta.visitors.valueOf().should.equal(2);
+ up.date.toString().should.equal(update.$set.date.toString());
+ up.published.should.eql(false);
+ up.mixed.x.should.equal('ECKS');
+ up.mixed.y.should.equal('why');
+ up.numbers.toObject().should.eql([5,7]);
+ up.owners.length.should.equal(1);
+ up.owners[0].toString().should.eql(id1.toString());
+ up.comments[0].body.should.equal('been there');
+ up.comments[1].body.should.equal('8');
+ // non-schema data was still stored in mongodb
+ should.strictEqual(1, up._doc.idontexist);
+
+ update5(post);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+
+ function update5 (post) {
+ var update = {
+ comments: [{ body: 'worked great' }]
+ , $set: {'numbers.1': 100}
+ , $inc: { idontexist: 1 }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(err, null);
+
+ // get the underlying doc
+ BlogPost.collection.findOne({ _id: post._id }, function (err, doc) {
+ should.strictEqual(err, null);
+
+ var up = new BlogPost;
+ up.init(doc);
+ up.comments.length.should.equal(1);
+ up.comments[0].body.should.equal('worked great');
+ should.strictEqual(true, !! doc.comments[0]._id);
+ up.meta.visitors.valueOf().should.equal(2);
+ up.mixed.x.should.equal('ECKS');
+ up.numbers.toObject().should.eql([5,100]);
+ up.numbers[1].valueOf().should.eql(100);
+
+ doc.idontexist.should.equal(2);
+ doc.numbers[1].should.eql(100);
+
+ update6(post);
+ });
+ });
+ }
+
+ function update6 (post) {
+ var update = {
+ $pushAll: { comments: [{ body: 'i am number 2' }, { body: 'i am number 3' }] }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(3);
+ ret.comments[1].body.should.equal('i am number 2');
+ should.strictEqual(true, !! ret.comments[1]._id);
+ ret.comments[1]._id.should.be.an.instanceof(DocumentObjectId)
+ ret.comments[2].body.should.equal('i am number 3');
+ should.strictEqual(true, !! ret.comments[2]._id);
+ ret.comments[2]._id.should.be.an.instanceof(DocumentObjectId)
+
+ update7(post);
+ })
+ });
+ }
+
+ // gh-542
+ function update7 (post) {
+ var update = {
+ $pull: { comments: { body: 'i am number 2' } }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(2);
+ ret.comments[0].body.should.equal('worked great');
+ ret.comments[0]._id.should.be.an.instanceof(DocumentObjectId)
+ ret.comments[1].body.should.equal('i am number 3');
+ ret.comments[1]._id.should.be.an.instanceof(DocumentObjectId)
+
+ update8(post);
+ })
+ });
+ }
+
+ // gh-479
+ function update8 (post) {
+ function a () {};
+ a.prototype.toString = function () { return 'MongoDB++' }
+ var crazy = new a;
+
+ var update = {
+ $addToSet: { 'comments.$.comments': { body: 'The Ring Of Power' } }
+ , $set: { 'comments.$.title': crazy }
+ }
+
+ BlogPost.update({ _id: post._id, 'comments.body': 'worked great' }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(2);
+ ret.comments[0].body.should.equal('worked great');
+ ret.comments[0].title.should.equal('MongoDB++');
+ should.strictEqual(true, !! ret.comments[0].comments);
+ ret.comments[0].comments.length.should.equal(1);
+ should.strictEqual(ret.comments[0].comments[0].body, 'The Ring Of Power');
+ ret.comments[0]._id.should.be.an.instanceof(DocumentObjectId)
+ ret.comments[0].comments[0]._id.should.be.an.instanceof(DocumentObjectId)
+ ret.comments[1].body.should.equal('i am number 3');
+ should.strictEqual(undefined, ret.comments[1].title);
+ ret.comments[1]._id.should.be.an.instanceof(DocumentObjectId)
+
+ update9(post);
+ })
+ });
+ }
+
+ // gh-479
+ function update9 (post) {
+ var update = {
+ $inc: { 'comments.$.newprop': '1' }
+ , $set: { date: (new Date).getTime() } // check for single val casting
+ }
+
+ BlogPost.update({ _id: post._id, 'comments.body': 'worked great' }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret._doc.comments[0]._doc.newprop.should.equal(1);
+ should.strictEqual(undefined, ret._doc.comments[1]._doc.newprop);
+ ret.date.should.be.an.instanceof(Date);
+ ret.date.toString().should.equal(update.$set.date.toString());
+
+ update10(post, ret);
+ })
+ });
+ }
+
+ // gh-545
+ function update10 (post, last) {
+ var owner = last.owners[0];
+
+ var update = {
+ $addToSet: { 'owners': owner }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.owners.length.should.equal(1);
+ ret.owners[0].toString().should.eql(owner.toString());
+
+ update11(post, ret);
+ })
+ });
+ }
+
+ // gh-545
+ function update11 (post, last) {
+ var owner = last.owners[0]
+ , newowner = new DocumentObjectId
+
+ var update = {
+ $addToSet: { 'owners': { $each: [owner, newowner] }}
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.owners.length.should.equal(2);
+ ret.owners[0].toString().should.eql(owner.toString());
+ ret.owners[1].toString().should.eql(newowner.toString());
+
+ update12(post, newowner);
+ })
+ });
+ }
+
+ // gh-574
+ function update12 (post, newowner) {
+ var update = {
+ $pop: { 'owners': -1 }
+ , $unset: { title: 1 }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.owners.length.should.equal(1);
+ ret.owners[0].toString().should.eql(newowner.toString());
+ should.strictEqual(undefined, ret.title);
+
+ update13(post, ret);
+ })
+ });
+ }
+
+ function update13 (post, ret) {
+ var update = {
+ $set: {
+ 'comments.0.comments.0.date': '11/5/2011'
+ , 'comments.1.body': 9000
+ }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(2);
+ ret.comments[0].body.should.equal('worked great');
+ ret.comments[1].body.should.equal('9000');
+ ret.comments[0].comments[0].date.toString().should.equal(new Date('11/5/2011').toString())
+ ret.comments[1].comments.length.should.equal(0);
+
+ update14(post, ret);
+ })
+ });
+ }
+
+ // gh-542
+ function update14 (post, ret) {
+ var update = {
+ $pull: { comments: { _id: ret.comments[0].id } }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(1);
+ ret.comments[0].body.should.equal('9000');
+ update15(post, ret);
+ })
+ });
+ }
+
+ function update15 (post, ret) {
+ var update = {
+ $pull: { comments: { body: { $in: [ret.comments[0].body] }} }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(0);
+ update16(post, ret);
+ })
+ });
+ }
+
+ function update16 (post, ret) {
+ ret.comments.$pushAll([{body: 'hi'}, {body:'there'}]);
+ ret.save(function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(2);
+
+ var update = {
+ $pull: { comments: { body: { $nin: ['there'] }} }
+ }
+
+ BlogPost.update({ _id: ret._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ db.close();
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(1);
+ })
+ });
+ })
+ });
+ }
+ },
+
+ // gh-699
+ 'Model._castUpdate should honor strict schemas': function () {
+ var db = start();
+ var S = db.model('UpdateStrictSchema');
+ db.close();
+
+ var doc = S.find()._castUpdate({ ignore: true });
+ Object.keys(doc.$set).length.should.equal(0);
+ },
+
+ 'Model.update should honor strict schemas': function () {
+ var db = start();
+ var S = db.model('UpdateStrictSchema');
+ var s = new S({ name: 'orange crush' });
+
+ s.save(function (err) {
+ should.strictEqual(null, err);
+
+ S.update({ _id: s._id }, { ignore: true }, function (err, affected) {
+ should.strictEqual(null, err);
+ affected.should.equal(1);
+
+ S.findById(s._id, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ should.not.exist(doc.ignore);
+ should.not.exist(doc._doc.ignore);
+ });
+ });
+ });
+ },
+
+ 'model.update passes number of affected documents': function () {
+ var db = start()
+ , B = db.model('BlogPost', 'wwwwowowo'+random())
+
+ B.create({ title: 'one'},{title:'two'},{title:'three'}, function (err) {
+ should.strictEqual(null, err);
+ B.update({}, { title: 'newtitle' }, { multi: true }, function (err, affected) {
+ db.close();
+ should.strictEqual(null, err);
+ affected.should.equal(3);
+ });
+ });
+ }
+
+}
diff --git a/node_modules/mongoose/test/namedscope.test.js b/node_modules/mongoose/test/namedscope.test.js
new file mode 100644
index 0000000..0160404
--- /dev/null
+++ b/node_modules/mongoose/test/namedscope.test.js
@@ -0,0 +1,253 @@
+//Query.prototype.where(criteria, callback)
+//Query.prototype.where(path, val, callback)
+//
+//UserNS.namedScope({
+// twenties: Query.where('age').gte(20).lt(30)
+// , male: Query.where('gender', 'male')
+// , lastLogin: Query.where('lastLogin').get(+new Date - (24 * 3600 * 1000))
+//});
+//
+//UserNS.find(twenties, male, active, function (err, found) {
+//});
+//
+//// twenties.male OR twenties.active
+//UserNS.twenties.male.OR.twenties.active.find(callback);
+//UserNS.find(twenties.male, twenties.active, function (err, found) {
+//});
+//
+//UserNS.find(olderThan(20).male, olderThan(30).active, function (err, found) {
+//});
+//UserNS.twenties.male.active.remove(callback);
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Schema = mongoose.Schema
+ , _24hours = 24 * 3600 * 1000;
+
+/**
+ * Setup.
+ */
+
+var UserNSSchema = new Schema({
+ age: Number
+ , gender: String
+ , lastLogin: Date
+});
+
+UserNSSchema.namedScope('olderThan', function (age) {
+ return this.where('age').gt(age);
+});
+
+UserNSSchema.namedScope('youngerThan', function (age) {
+ return this.where('age').lt(age);
+});
+
+UserNSSchema.namedScope('twenties').olderThan(19).youngerThan(30);
+
+UserNSSchema.namedScope('male').where('gender', 'male');
+
+UserNSSchema.namedScope('female').where('gender', 'female');
+
+UserNSSchema.namedScope('active', function () {
+ return this.where('lastLogin').gte(+new Date - _24hours)
+});
+
+mongoose.model('UserNS', UserNSSchema);
+
+// TODO Add in tests for using named scopes where findOne, update, remove
+module.exports = {
+ 'basic named scopes should work, for find': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {gender: 'male'}
+ , {gender: 'male'}
+ , {gender: 'female'}
+ , function (err, _) {
+ should.strictEqual(err, null);
+ UserNS.male.find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ });
+ }
+ );
+ },
+ 'dynamic named scopes should work, for find': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {age: 21}
+ , {age: 22}
+ , {age: 19}
+ , function (err, _) {
+ should.strictEqual(err, null);
+ UserNS.olderThan(20).find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ });
+ }
+ );
+ },
+ 'named scopes built on top of dynamic named scopes should work, for find': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {age: 21}
+ , {age: 22}
+ , {age: 19}
+ , function (err, _) {
+ should.strictEqual(err, null);
+ UserNS.twenties.find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ });
+ }
+ );
+ },
+ 'chaining named scopes should work, for find': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {age: 21, gender: 'male', lastLogin: (+new Date) - _24hours - 3600}
+ , {age: 45, gender: 'male', lastLogin: +new Date}
+ , {age: 50, gender: 'female', lastLogin: +new Date}
+ , function (err, _, match, _) {
+ should.strictEqual(err, null);
+ UserNS.olderThan(40).active.male.find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(match._id);
+ });
+ }
+ );
+ },
+
+ 'basic named scopes should work, for remove': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {gender: 'male'}
+ , {gender: 'male'}
+ , {gender: 'female'}
+ , function (err, _) {
+ UserNS.male.remove( function (err) {
+ should.strictEqual(!!err, false);
+ UserNS.male.find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(0);
+ });
+ });
+ }
+ );
+ },
+
+ // TODO multi-updates
+ 'basic named scopes should work, for update': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {gender: 'male'}
+ , {gender: 'male'}
+ , {gender: 'female'}
+ , function (err, male1, male2, female1) {
+ should.strictEqual(err, null);
+ UserNS.male.update({gender: 'female'}, function (err) {
+ should.strictEqual(err, null);
+ UserNS.female.find( function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ UserNS.male.find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ });
+ });
+ });
+ }
+ );
+ },
+
+ 'chained named scopes should work, for findOne': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {age: 100, gender: 'male'}
+ , function (err, maleCentenarian) {
+ should.strictEqual(err, null);
+ UserNS.male.olderThan(99).findOne( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(maleCentenarian._id);
+ });
+ }
+ );
+ },
+
+ 'hybrid use of chained named scopes and ad hoc querying should work': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {age: 100, gender: 'female'}
+ , function (err, femaleCentenarian) {
+ should.strictEqual(null, err);
+ UserNS.female.where('age').gt(99).findOne( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(femaleCentenarian._id);
+ });
+ }
+ );
+ },
+// 'using chained named scopes in a find': function () {
+// var db = start()
+// , UserNS = db.model('UserNS', 'users_' + random());
+// UserNS.create({age: 21, gender: 'male', lastLogin: (+new Date) - _24hours - 3600}, function (err, _) {
+// should.strictEqual(err, null);
+// UserNS.create({age: 45, gender: 'male', lastLogin: +new Date}, function (err, match) {
+// should.strictEqual(err, null);
+// UserNS.create({age: 50, gender: 'female', lastLogin: +new Date}, function (err, _) {
+// should.strictEqual(err, null);
+// UserNS.find(olderThan(40).active.male, function (err, found) {
+// db.close();
+// should.strictEqual(err, null);
+// found.should.have.length(1);
+// found[0]._id.should.eql(match._id);
+// });
+// });
+// });
+// });
+// },
+// 'using multiple chained named scopes in a find to do an OR': function () {
+// var db = start()
+// , UserNS = db.model('UserNS', collection);
+// var db = start()
+// , UserNS = db.model('UserNS', collection);
+// UserNS.create(
+// {age: 21, gender: 'male', lastLogin: (+new Date) - _24hours - 3600}
+// , {age: 45, gender: 'male', lastLogin: +new Date}
+// , {age: 50, gender: 'female', lastLogin: +new Date}
+// , {age: 35, gender: 'female', lastLogin: +new Date}
+// , function (err, a, b, c, d) {
+// should.strictEqual(err, null);
+// UserNS.find(twenties.active.male, thirties.active.female, function (err, found) {
+// db.close();
+// should.strictEqual(err, null);
+// found.should.have.length(2);
+// });
+// }
+// );
+// },
+};
diff --git a/node_modules/mongoose/test/promise.test.js b/node_modules/mongoose/test/promise.test.js
new file mode 100644
index 0000000..6d23a55
--- /dev/null
+++ b/node_modules/mongoose/test/promise.test.js
@@ -0,0 +1,167 @@
+
+/**
+ * Module dependencies.
+ */
+
+var should = require('should')
+ , Promise = require('../lib/promise');
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that events fire right away after complete()ing': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.on('complete', function (a, b) {
+ a.should.eql('1');
+ b.should.eql('2');
+ called++;
+ });
+
+ promise.complete('1', '2');
+
+ promise.on('complete', function (a, b) {
+ a.should.eql('1');
+ b.should.eql('2');
+ called++;
+ });
+
+ beforeExit(function () {
+ called.should.eql(2);
+ });
+ },
+
+ 'test that events fire right away after error()ing': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.on('err', function (err) {
+ err.should.be.an.instanceof(Error);
+ called++;
+ });
+
+ promise.error(new Error('booyah'));
+
+ promise.on('err', function (err) {
+ err.should.be.an.instanceof(Error);
+ called++;
+ });
+
+ beforeExit(function () {
+ called.should.eql(2);
+ });
+ },
+
+ 'test errback+callback from constructor': function (beforeExit) {
+ var promise = new Promise(function (err) {
+ err.should.be.an.instanceof(Error);
+ called++;
+ })
+ , called = 0;
+
+ promise.error(new Error('dawg'));
+
+ beforeExit(function () {
+ called.should.eql(1);
+ });
+ },
+
+ 'test errback+callback after complete()ing': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.complete('woot');
+
+ promise.addBack(function (err, data){
+ data.should.eql('woot');
+ called++;
+ });
+
+ promise.addBack(function (err, data){
+ should.strictEqual(err, null);
+ called++;
+ });
+
+ beforeExit(function () {
+ called.should.eql(2);
+ });
+ },
+
+ 'test errback+callback after error()ing': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.error(new Error('woot'));
+
+ promise.addBack(function (err){
+ err.should.be.an.instanceof(Error);
+ called++;
+ });
+
+ promise.addBack(function (err){
+ err.should.be.an.instanceof(Error);
+ called++;
+ });
+
+ beforeExit(function () {
+ called.should.eql(2);
+ });
+ },
+
+ 'test addCallback shortcut': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.addCallback(function (woot) {
+ should.strictEqual(woot, undefined);
+ called++;
+ });
+
+ promise.complete();
+
+ beforeExit(function () {
+ called.should.eql(1);
+ });
+ },
+
+ 'test addErrback shortcut': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.addErrback(function (err) {
+ err.should.be.an.instanceof(Error);
+ called++;
+ });
+
+ promise.error(new Error);
+
+ beforeExit(function () {
+ called.should.eql(1);
+ });
+ },
+
+ 'test return value of #on()': function () {
+ var promise = new Promise()
+ promise.on('jump', function(){}).should.be.an.instanceof(Promise);
+ },
+
+ 'test return value of #addCallback()': function () {
+ var promise = new Promise()
+ promise.addCallback(function(){}).should.be.an.instanceof(Promise);
+ },
+
+ 'test return value of #addErrback()': function () {
+ var promise = new Promise()
+ promise.addErrback(function(){}).should.be.an.instanceof(Promise);
+ },
+
+ 'test return value of #addBack()': function () {
+ var promise = new Promise()
+ promise.addBack(function(){}).should.be.an.instanceof(Promise);
+ }
+
+};
diff --git a/node_modules/mongoose/test/query.test.js b/node_modules/mongoose/test/query.test.js
new file mode 100644
index 0000000..427d6cc
--- /dev/null
+++ b/node_modules/mongoose/test/query.test.js
@@ -0,0 +1,890 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , Query = require('../lib/query')
+ , mongoose = start.mongoose
+ , DocumentObjectId = mongoose.Types.ObjectId
+ , Schema = mongoose.Schema
+ , should = require('should')
+
+var Comment = new Schema({
+ text: String
+});
+
+var Product = new Schema({
+ tags: {} // mixed
+ , array: Array
+ , ids: [Schema.ObjectId]
+ , strings: [String]
+ , numbers: [Number]
+ , comments: [Comment]
+});
+
+mongoose.model('Product', Product);
+mongoose.model('Comment', Comment);
+
+/**
+ * Test.
+ */
+
+module.exports = {
+ 'test query.fields({a: 1, b: 1, c: 1})': function () {
+ var query = new Query();
+ query.fields({a: 1, b: 1, c: 1});
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ 'test query.fields({only: "a b c"})': function () {
+ var query = new Query();
+ query.fields({only: "a b c"});
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ 'test query.fields({only: ["a", "b", "c"]})': function () {
+ var query = new Query();
+ query.fields({only: ['a', 'b', 'c']});
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ 'test query.fields("a b c")': function () {
+ var query = new Query();
+ query.fields("a b c");
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ 'test query.fields("a", "b", "c")': function () {
+ var query = new Query();
+ query.fields('a', 'b', 'c');
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ "test query.fields(['a', 'b', 'c'])": function () {
+ var query = new Query();
+ query.fields(['a', 'b', 'c']);
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ "Query#fields should not over-ride fields set in prior calls to Query#fields": function () {
+ var query = new Query();
+ query.fields('a');
+ query._fields.should.eql({a: 1});
+ query.fields('b');
+ query._fields.should.eql({a: 1, b: 1});
+ },
+// "Query#fields should be able to over-ride fields set in prior calls to Query#fields if you specify override": function () {
+// var query = new Query();
+// query.fields('a');
+// query._fields.should.eql({a: 1});
+// query.override.fields('b');
+// query._fields.should.eql({b: 1});
+// }
+
+ "test query.only('a b c')": function () {
+ var query = new Query();
+ query.only("a b c");
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ "test query.only('a', 'b', 'c')": function () {
+ var query = new Query();
+ query.only('a', 'b', 'c');
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ "test query.only('a', 'b', 'c')": function () {
+ var query = new Query();
+ query.only(['a', 'b', 'c']);
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ "Query#only should not over-ride fields set in prior calls to Query#only": function () {
+ var query = new Query();
+ query.only('a');
+ query._fields.should.eql({a: 1});
+ query.only('b');
+ query._fields.should.eql({a: 1, b: 1});
+ },
+
+ "test query.exclude('a b c')": function () {
+ var query = new Query();
+ query.exclude("a b c");
+ query._fields.should.eql({a: 0, b: 0, c: 0});
+ },
+ "test query.exclude('a', 'b', 'c')": function () {
+ var query = new Query();
+ query.exclude('a', 'b', 'c');
+ query._fields.should.eql({a: 0, b: 0, c: 0});
+ },
+ "test query.exclude('a', 'b', 'c')": function () {
+ var query = new Query();
+ query.exclude(['a', 'b', 'c']);
+ query._fields.should.eql({a: 0, b: 0, c: 0});
+ },
+ "Query#exclude should not over-ride fields set in prior calls to Query#exclude": function () {
+ var query = new Query();
+ query.exclude('a');
+ query._fields.should.eql({a: 0});
+ query.exclude('b');
+ query._fields.should.eql({a: 0, b: 0});
+ },
+
+ 'test setting a condition via where': function () {
+ var query = new Query();
+ query.where('name', 'guillermo');
+ query._conditions.should.eql({name: 'guillermo'});
+ },
+
+ 'setting a value via equals': function () {
+ var query = new Query();
+ query.where('name').equals('guillermo');
+ query._conditions.should.eql({name: 'guillermo'});
+ },
+
+ 'test Query#gte where 2 arguments': function () {
+ var query = new Query();
+ query.gte('age', 18);
+ query._conditions.should.eql({age: {$gte: 18}});
+ },
+
+ 'test Query#gt where 2 arguments': function () {
+ var query = new Query();
+ query.gt('age', 17);
+ query._conditions.should.eql({age: {$gt: 17}});
+ },
+
+ 'test Query#lte where 2 arguments': function () {
+ var query = new Query();
+ query.lte('age', 65);
+ query._conditions.should.eql({age: {$lte: 65}});
+ },
+
+ 'test Query#lt where 2 arguments': function () {
+ var query = new Query();
+ query.lt('age', 66);
+ query._conditions.should.eql({age: {$lt: 66}});
+ },
+
+ 'test Query#gte where 1 argument': function () {
+ var query = new Query();
+ query.where("age").gte(18);
+ query._conditions.should.eql({age: {$gte: 18}});
+ },
+
+ 'test Query#gt where 1 argument': function () {
+ var query = new Query();
+ query.where("age").gt(17);
+ query._conditions.should.eql({age: {$gt: 17}});
+ },
+
+ 'test Query#lte where 1 argument': function () {
+ var query = new Query();
+ query.where("age").lte(65);
+ query._conditions.should.eql({age: {$lte: 65}});
+ },
+
+ 'test Query#lt where 1 argument': function () {
+ var query = new Query();
+ query.where("age").lt(66);
+ query._conditions.should.eql({age: {$lt: 66}});
+ },
+
+ 'test combined Query#lt and Query#gt': function () {
+ var query = new Query();
+ query.where("age").lt(66).gt(17);
+ query._conditions.should.eql({age: {$lt: 66, $gt: 17}});
+ },
+
+ 'test Query#lt on one path and Query#gt on another path on the same query': function () {
+ var query = new Query();
+ query
+ .where("age").lt(66)
+ .where("height").gt(5);
+ query._conditions.should.eql({age: {$lt: 66}, height: {$gt: 5}});
+ },
+
+ 'test Query#ne where 2 arguments': function () {
+ var query = new Query();
+ query.ne('age', 21);
+ query._conditions.should.eql({age: {$ne: 21}});
+ },
+
+ 'test Query#gte where 1 argument': function () {
+ var query = new Query();
+ query.where("age").ne(21);
+ query._conditions.should.eql({age: {$ne: 21}});
+ },
+
+ 'test Query#ne alias Query#notEqualTo': function () {
+ var query = new Query();
+ query.where('age').notEqualTo(21);
+ query._conditions.should.eql({age: {$ne: 21}});
+
+ query = new Query();
+ query.notEqualTo('age', 21);
+ query._conditions.should.eql({age: {$ne: 21}});
+ },
+
+ 'test Query#in where 2 arguments': function () {
+ var query = new Query();
+ query.in('age', [21, 25, 30]);
+ query._conditions.should.eql({age: {$in: [21, 25, 30]}});
+ },
+
+ 'test Query#in where 1 argument': function () {
+ var query = new Query();
+ query.where("age").in([21, 25, 30]);
+ query._conditions.should.eql({age: {$in: [21, 25, 30]}});
+ },
+
+ 'test Query#in where a non-array value not via where': function () {
+ var query = new Query();
+ query.in('age', 21);
+ query._conditions.should.eql({age: {$in: 21}});
+ },
+
+ 'test Query#in where a non-array value via where': function () {
+ var query = new Query();
+ query.where('age').in(21);
+ query._conditions.should.eql({age: {$in: 21}});
+ },
+
+ 'test Query#nin where 2 arguments': function () {
+ var query = new Query();
+ query.nin('age', [21, 25, 30]);
+ query._conditions.should.eql({age: {$nin: [21, 25, 30]}});
+ },
+
+ 'test Query#nin where 1 argument': function () {
+ var query = new Query();
+ query.where("age").nin([21, 25, 30]);
+ query._conditions.should.eql({age: {$nin: [21, 25, 30]}});
+ },
+
+ 'test Query#nin where a non-array value not via where': function () {
+ var query = new Query();
+ query.nin('age', 21);
+ query._conditions.should.eql({age: {$nin: 21}});
+ },
+
+ 'test Query#nin where a non-array value via where': function () {
+ var query = new Query();
+ query.where('age').nin(21);
+ query._conditions.should.eql({age: {$nin: 21}});
+ },
+
+ 'test Query#mod not via where, where [a, b] param': function () {
+ var query = new Query();
+ query.mod('age', [5, 2]);
+ query._conditions.should.eql({age: {$mod: [5, 2]}});
+ },
+
+ 'test Query#mod not via where, where a and b params': function () {
+ var query = new Query();
+ query.mod('age', 5, 2);
+ query._conditions.should.eql({age: {$mod: [5, 2]}});
+ },
+
+ 'test Query#mod via where, where [a, b] param': function () {
+ var query = new Query();
+ query.where("age").mod([5, 2]);
+ query._conditions.should.eql({age: {$mod: [5, 2]}});
+ },
+
+ 'test Query#mod via where, where a and b params': function () {
+ var query = new Query();
+ query.where("age").mod(5, 2);
+ query._conditions.should.eql({age: {$mod: [5, 2]}});
+ },
+
+ 'test Query#near via where, where [lat, long] param': function () {
+ var query = new Query();
+ query.where('checkin').near([40, -72]);
+ query._conditions.should.eql({checkin: {$near: [40, -72]}});
+ },
+
+ 'test Query#near via where, where lat and long params': function () {
+ var query = new Query();
+ query.where('checkin').near(40, -72);
+ query._conditions.should.eql({checkin: {$near: [40, -72]}});
+ },
+
+ 'test Query#near not via where, where [lat, long] param': function () {
+ var query = new Query();
+ query.near('checkin', [40, -72]);
+ query._conditions.should.eql({checkin: {$near: [40, -72]}});
+ },
+
+ 'test Query#near not via where, where lat and long params': function () {
+ var query = new Query();
+ query.near('checkin', 40, -72);
+ query._conditions.should.eql({checkin: {$near: [40, -72]}});
+ },
+
+ 'test Query#maxDistance via where': function () {
+ var query = new Query();
+ query.where('checkin').near([40, -72]).maxDistance(1);
+ query._conditions.should.eql({checkin: {$near: [40, -72], $maxDistance: 1}});
+ query = new Query();
+ query.where('checkin').near([40, -72]).$maxDistance(1);
+ query._conditions.should.eql({checkin: {$near: [40, -72], $maxDistance: 1}});
+ },
+
+ 'test Query#wherein.box not via where': function () {
+ var query = new Query();
+ query.wherein.box('gps', {ll: [5, 25], ur: [10, 30]});
+ query._conditions.should.eql({gps: {$within: {$box: [[5, 25], [10, 30]]}}});
+ },
+
+ 'test Query#wherein.box via where': function () {
+ var query = new Query();
+ query.where('gps').wherein.box({ll: [5, 25], ur: [10, 30]});
+ query._conditions.should.eql({gps: {$within: {$box: [[5, 25], [10, 30]]}}});
+ },
+
+ 'test Query#wherein.center not via where': function () {
+ var query = new Query();
+ query.wherein.center('gps', {center: [5, 25], radius: 5});
+ query._conditions.should.eql({gps: {$within: {$center: [[5, 25], 5]}}});
+ },
+
+ 'test Query#wherein.center not via where': function () {
+ var query = new Query();
+ query.where('gps').wherein.center({center: [5, 25], radius: 5});
+ query._conditions.should.eql({gps: {$within: {$center: [[5, 25], 5]}}});
+ },
+
+ 'test Query#exists where 0 arguments via where': function () {
+ var query = new Query();
+ query.where("username").exists();
+ query._conditions.should.eql({username: {$exists: true}});
+ },
+
+ 'test Query#exists where 1 argument via where': function () {
+ var query = new Query();
+ query.where("username").exists(false);
+ query._conditions.should.eql({username: {$exists: false}});
+ },
+
+ 'test Query#exists where 1 argument not via where': function () {
+ var query = new Query();
+ query.exists('username');
+ query._conditions.should.eql({username: {$exists: true}});
+ },
+
+ 'test Query#exists where 1 argument not via where': function () {
+ var query = new Query();
+ query.exists("username", false);
+ query._conditions.should.eql({username: {$exists: false}});
+ },
+
+ // TODO $not
+
+ 'test Query#all via where': function () {
+ var query = new Query();
+ query.where('pets').all(['dog', 'cat', 'ferret']);
+ query._conditions.should.eql({pets: {$all: ['dog', 'cat', 'ferret']}});
+ },
+
+ 'test Query#all not via where': function () {
+ var query = new Query();
+ query.all('pets', ['dog', 'cat', 'ferret']);
+ query._conditions.should.eql({pets: {$all: ['dog', 'cat', 'ferret']}});
+ },
+
+ 'test strict array equivalence condition via Query#find': function () {
+ var query = new Query();
+ query.find({'pets': ['dog', 'cat', 'ferret']});
+ query._conditions.should.eql({pets: ['dog', 'cat', 'ferret']});
+ },
+
+ '#find with no args should not throw': function () {
+ var threw = false;
+ var q = new Query();
+
+ try {
+ q.find();
+ } catch (err) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+ },
+
+ // TODO Check key.index queries
+
+ 'test Query#size via where': function () {
+ var query = new Query();
+ query.where('collection').size(5);
+ query._conditions.should.eql({collection: {$size: 5}});
+ },
+
+ 'test Query#size not via where': function () {
+ var query = new Query();
+ query.size('collection', 5);
+ query._conditions.should.eql({collection: {$size: 5}});
+ },
+
+ 'test Query#slice via where, where just positive limit param': function () {
+ var query = new Query();
+ query.where('collection').slice(5);
+ query._fields.should.eql({collection: {$slice: 5}});
+ },
+
+ 'test Query#slice via where, where just negative limit param': function () {
+ var query = new Query();
+ query.where('collection').slice(-5);
+ query._fields.should.eql({collection: {$slice: -5}});
+ },
+
+ 'test Query#slice via where, where [skip, limit] param': function () {
+ var query = new Query();
+ query.where('collection').slice([14, 10]); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+ 'test Query#slice via where, where skip and limit params': function () {
+ var query = new Query();
+ query.where('collection').slice(14, 10); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+ 'test Query#slice via where, where just positive limit param': function () {
+ var query = new Query();
+ query.where('collection').slice(5);
+ query._fields.should.eql({collection: {$slice: 5}});
+ },
+
+ 'test Query#slice via where, where just negative limit param': function () {
+ var query = new Query();
+ query.where('collection').slice(-5);
+ query._fields.should.eql({collection: {$slice: -5}});
+ },
+
+ 'test Query#slice via where, where the [skip, limit] param': function () {
+ var query = new Query();
+ query.where('collection').slice([14, 10]); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+ 'test Query#slice via where, where the skip and limit params': function () {
+ var query = new Query();
+ query.where('collection').slice(14, 10); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+
+ 'test Query#slice not via where, where just positive limit param': function () {
+ var query = new Query();
+ query.slice('collection', 5);
+ query._fields.should.eql({collection: {$slice: 5}});
+ },
+
+ 'test Query#slice not via where, where just negative limit param': function () {
+ var query = new Query();
+ query.slice('collection', -5);
+ query._fields.should.eql({collection: {$slice: -5}});
+ },
+
+ 'test Query#slice not via where, where [skip, limit] param': function () {
+ var query = new Query();
+ query.slice('collection', [14, 10]); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+ 'test Query#slice not via where, where skip and limit params': function () {
+ var query = new Query();
+ query.slice('collection', 14, 10); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+ 'test Query#elemMatch not via where': function () {
+ var query = new Query();
+ query.elemMatch('comments', {author: 'bnoguchi', votes: {$gte: 5}});
+ query._conditions.should.eql({comments: {$elemMatch: {author: 'bnoguchi', votes: {$gte: 5}}}});
+ },
+
+ 'test Query#elemMatch not via where, where block notation': function () {
+ var query = new Query();
+ query.elemMatch('comments', function (elem) {
+ elem.where('author', 'bnoguchi')
+ elem.where('votes').gte(5);
+ });
+ query._conditions.should.eql({comments: {$elemMatch: {author: 'bnoguchi', votes: {$gte: 5}}}});
+ },
+
+ 'test Query#elemMatch via where': function () {
+ var query = new Query();
+ query.where('comments').elemMatch({author: 'bnoguchi', votes: {$gte: 5}});
+ query._conditions.should.eql({comments: {$elemMatch: {author: 'bnoguchi', votes: {$gte: 5}}}});
+ },
+
+ 'test Query#elemMatch via where, where block notation': function () {
+ var query = new Query();
+ query.where('comments').elemMatch(function (elem) {
+ elem.where('author', 'bnoguchi')
+ elem.where('votes').gte(5);
+ });
+ query._conditions.should.eql({comments: {$elemMatch: {author: 'bnoguchi', votes: {$gte: 5}}}});
+ },
+
+
+ 'test Query#$where where a function arg': function () {
+ var query = new Query();
+ function filter () {
+ return this.lastName === this.firstName;
+ }
+ query.$where(filter);
+ query._conditions.should.eql({$where: filter});
+ },
+
+ 'test Query#where where a javascript string arg': function () {
+ var query = new Query();
+ query.$where('this.lastName === this.firstName');
+ query._conditions.should.eql({$where: 'this.lastName === this.firstName'});
+ },
+
+ 'test Query#limit': function () {
+ var query = new Query();
+ query.limit(5);
+ query.options.limit.should.equal(5);
+ },
+
+ 'test Query#skip': function () {
+ var query = new Query();
+ query.skip(9);
+ query.options.skip.should.equal(9);
+ },
+
+ 'test Query#sort': function () {
+ var query = new Query();
+ query.sort('a', 1, 'c', -1, 'b', 1);
+ query.options.sort.should.eql([['a', 1], ['c', -1], ['b', 1]]);
+ },
+
+ 'test Query#asc and Query#desc': function () {
+ var query = new Query();
+ query.asc('a', 'z').desc('c', 'v').asc('b');
+ query.options.sort.should.eql([['a', 1], ['z', 1], ['c', -1], ['v', -1], ['b', 1]]);
+ },
+
+ 'Query#or': function () {
+ var query = new Query;
+ query.find({ $or: [{x:1},{x:2}] });
+ query._conditions.$or.length.should.equal(2);
+ query.$or([{y:"We're under attack"}, {z:47}]);
+ query._conditions.$or.length.should.equal(4);
+ query._conditions.$or[3].z.should.equal(47);
+ query.or({z:"phew"});
+ query._conditions.$or.length.should.equal(5);
+ query._conditions.$or[3].z.should.equal(47);
+ query._conditions.$or[4].z.should.equal("phew");
+ },
+
+ 'test running an empty Query should not throw': function () {
+ var query = new Query();
+ var threw = false;
+
+ try {
+ query.exec();
+ } catch (err) {
+ threw = true;
+ }
+
+ threw.should.eql(false);
+ },
+
+ 'test casting an array set to mixed type works': function () {
+ var query = new Query();
+ var db = start();
+ var Product = db.model('Product');
+ var params = { _id: new DocumentObjectId, tags: { $in: [ 4, 8, 15, 16 ] }};
+
+ query.cast(Product, params);
+
+ params.tags.$in.should.eql([4,8,15,16]);
+ db.close();
+ },
+
+ //'throwing inside a query callback should not execute the callback again': function () {
+ //var query = new Query();
+ //var db = start();
+ //var Product = db.model('Product');
+
+ //var threw = false;
+ //Product.find({}, function (err) {
+ //if (!threw) {
+ //db.close();
+ //threw = true;
+ //throw new Error("Double callback");
+ //}
+
+ //should.strictEqual(err, null, 'Double callback detected');
+ //});
+ //},
+
+ 'Query#find $ne should not cast single value to array for schematype of Array': function () {
+ var query = new Query();
+ var db = start();
+ var Product = db.model('Product');
+ var Comment = db.model('Comment');
+ db.close();
+
+ var id = new DocumentObjectId;
+ var castedComment = { _id: id, text: 'hello there' };
+ var comment = new Comment(castedComment);
+
+ var params = {
+ array: { $ne: 5 }
+ , ids: { $ne: id }
+ , comments: { $ne: comment }
+ , strings: { $ne: 'Hi there' }
+ , numbers: { $ne: 10000 }
+ };
+
+ query.cast(Product, params);
+ params.array.$ne.should.equal(5);
+ params.ids.$ne.should.eql(id);
+ params.comments.$ne._id.toHexString();
+ params.comments.$ne.should.eql(castedComment);
+ params.strings.$ne.should.eql('Hi there');
+ params.numbers.$ne.should.eql(10000);
+
+ params.array.$ne = [5];
+ params.ids.$ne = [id];
+ params.comments.$ne = [comment];
+ params.strings.$ne = ['Hi there'];
+ params.numbers.$ne = [10000];
+ query.cast(Product, params);
+ params.array.$ne.should.be.instanceof(Array);
+ params.array.$ne[0].should.eql(5);
+ params.ids.$ne.should.be.instanceof(Array);
+ params.ids.$ne[0].toString().should.eql(id.toString());
+ params.comments.$ne.should.be.instanceof(Array);
+ params.comments.$ne[0].should.eql(castedComment);
+ params.strings.$ne.should.be.instanceof(Array);
+ params.strings.$ne[0].should.eql('Hi there');
+ params.numbers.$ne.should.be.instanceof(Array);
+ params.numbers.$ne[0].should.eql(10000);
+ },
+
+ 'Querying a subdocument array with $ne: null should not throw': function () {
+ var query = new Query();
+ var db = start();
+ var Product = db.model('Product');
+ var Comment = db.model('Comment');
+ db.close();
+
+ var params = {
+ comments: { $ne: null }
+ };
+
+ query.cast(Product, params);
+ should.strictEqual(params.comments.$ne, null);
+ },
+
+ 'Query#find should not cast single value to array for schematype of Array': function () {
+ var query = new Query();
+ var db = start();
+ var Product = db.model('Product');
+ var Comment = db.model('Comment');
+ db.close();
+
+ var id = new DocumentObjectId;
+ var castedComment = { _id: id, text: 'hello there' };
+ var comment = new Comment(castedComment);
+
+ var params = {
+ array: 5
+ , ids: id
+ , comments: comment
+ , strings: 'Hi there'
+ , numbers: 10000
+ };
+
+ query.cast(Product, params);
+ params.array.should.equal(5);
+ params.ids.should.eql(id);
+ params.comments._id.toHexString();
+ params.comments.should.eql(castedComment);
+ params.strings.should.eql('Hi there');
+ params.numbers.should.eql(10000);
+
+ params.array = [5];
+ params.ids = [id];
+ params.comments = [comment];
+ params.strings = ['Hi there'];
+ params.numbers = [10000];
+ query.cast(Product, params);
+ params.array.should.be.instanceof(Array);
+ params.array[0].should.eql(5);
+ params.ids.should.be.instanceof(Array);
+ params.ids[0].toString().should.eql(id.toString());
+ params.comments.should.be.instanceof(Array);
+ params.comments[0].should.eql(castedComment);
+ params.strings.should.be.instanceof(Array);
+ params.strings[0].should.eql('Hi there');
+ params.numbers.should.be.instanceof(Array);
+ params.numbers[0].should.eql(10000);
+ },
+
+ 'distinct Query op should be "distinct"': function () {
+ var db = start();
+ var query = new Query();
+ var Product = db.model('Product');
+ new Query().bind(Product, 'distinct').distinct('blah', function(){
+ db.close();
+ }).op.should.equal('distinct');
+ },
+
+ 'Query without a callback works': function () {
+ var db = start();
+ var query = new Query();
+ var Product = db.model('Product');
+ new Query().bind(Product, 'count').count();
+ setTimeout(db.close.bind(db), 1000);
+ },
+
+ '#findOne should set the op when callback is passed': function () {
+ var db = start();
+ var query = new Query();
+ var Product = db.model('Product');
+ var q = new Query().bind(Product, 'distinct');
+ q.op.should.equal('distinct');
+ q.findOne();
+ q.op.should.equal('findOne');
+ db.close();
+ },
+
+ 'querying/updating with model instance containing embedded docs should work (#454)': function () {
+ var db = start();
+ var Product = db.model('Product');
+
+ var proddoc = { comments: [{ text: 'hello' }] };
+ var prod2doc = { comments: [{ text: 'goodbye' }] };
+
+ var prod = new Product(proddoc);
+ var prod2 = new Product(prod2doc);
+
+ prod.save(function (err) {
+ should.strictEqual(err, null);
+
+ Product.findOne(prod, function (err, product) {
+ should.strictEqual(err, null);
+ product.comments.length.should.equal(1);
+ product.comments[0].text.should.equal('hello');
+
+ Product.update(product, prod2doc, function (err) {
+ should.strictEqual(err, null);
+
+ Product.collection.findOne({ _id: product._id }, function (err, doc) {
+ db.close();
+ should.strictEqual(err, null);
+ doc.comments.length.should.equal(1);
+ // ensure hidden private props were not saved to db
+ doc.comments[0].should.not.have.ownProperty('parentArr');
+ doc.comments[0].text.should.equal('goodbye');
+ });
+ });
+ });
+ });
+ },
+
+ 'optionsForExecute should retain key order': function () {
+ // this is important for query hints
+ var hint = { x: 1, y: 1, z: 1 };
+ var a = JSON.stringify({ hint: hint, safe: true});
+
+ var q = new Query;
+ q.hint(hint);
+
+ var options = q._optionsForExec({ options: { safe: true } });
+
+ a.should.equal(JSON.stringify(options));
+ },
+
+ // Advanced Query options
+
+ 'test Query#maxscan': function () {
+ var query = new Query();
+ query.maxscan(100);
+ query.options.maxscan.should.equal(100);
+ },
+
+ 'test Query#slaveOk': function () {
+ var query = new Query();
+ query.slaveOk();
+ query.options.slaveOk.should.be.true;
+
+ var query = new Query();
+ query.slaveOk(true);
+ query.options.slaveOk.should.be.true;
+
+ var query = new Query();
+ query.slaveOk(false);
+ query.options.slaveOk.should.be.false;
+ },
+
+ 'test Query#tailable': function () {
+ var query = new Query();
+ query.tailable();
+ query.options.tailable.should.be.true;
+
+ var query = new Query();
+ query.tailable(true);
+ query.options.tailable.should.be.true;
+
+ var query = new Query();
+ query.tailable(false);
+ query.options.tailable.should.be.false;
+ },
+
+ 'Query#comment': function () {
+ var query = new Query;
+ 'function'.should.equal(typeof query.comment);
+ query.comment('Lowpass is more fun').should.equal(query);
+ query.options.comment.should.equal('Lowpass is more fun');
+ },
+
+ 'test Query#hint': function () {
+ var query = new Query();
+ query.hint('indexAttributeA', 1, 'indexAttributeB', -1);
+ query.options.hint.should.eql({'indexAttributeA': 1, 'indexAttributeB': -1});
+
+ var query2 = new Query();
+ query2.hint({'indexAttributeA': 1, 'indexAttributeB': -1});
+ query2.options.hint.should.eql({'indexAttributeA': 1, 'indexAttributeB': -1});
+
+ var query3 = new Query();
+ query3.hint('indexAttributeA');
+ query3.options.hint.should.eql({});
+ },
+
+ 'test Query#snapshot': function () {
+ var query = new Query();
+ query.snapshot(true);
+ query.options.snapshot.should.be.true;
+ },
+
+ 'test Query#batchSize': function () {
+ var query = new Query();
+ query.batchSize(10);
+ query.options.batchSize.should.equal(10);
+ },
+
+ 'empty updates are not run': function () {
+ var q = new Query;
+ ;(!!q._castUpdate({})).should.be.false;
+ }
+
+ // TODO
+// 'test Query#min': function () {
+// var query = new Query();
+// query.min(10);
+// query.options.min.should.equal(10);
+// },
+//
+ //TODO
+// 'test Query#max': function () {
+// var query = new Query();
+// query.max(100);
+// query.options.max.should.equal(100);
+// },
+
+ // TODO
+// 'test Query#explain': function () {
+// }
+};
diff --git a/node_modules/mongoose/test/schema.onthefly.test.js b/node_modules/mongoose/test/schema.onthefly.test.js
new file mode 100644
index 0000000..d038d78
--- /dev/null
+++ b/node_modules/mongoose/test/schema.onthefly.test.js
@@ -0,0 +1,105 @@
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Schema = mongoose.Schema
+ , ObjectId = Schema.ObjectId;
+
+/**
+ * Setup.
+ */
+
+var DecoratedSchema = new Schema({
+ title : String
+});
+
+mongoose.model('Decorated', DecoratedSchema);
+
+var collection = 'decorated_' + random();
+
+module.exports = {
+ 'setting on the fly schemas should cache the type schema and cast values appropriately': function () {
+ var db = start()
+ , Decorated = db.model('Decorated', collection);
+
+ var post = new Decorated();
+ post.set('adhoc', '9', Number);
+ post.get('adhoc').valueOf().should.eql(9);
+ db.close();
+ },
+
+ 'on the fly schemas should be local to the particular document': function () {
+ var db = start()
+ , Decorated = db.model('Decorated', collection);
+
+ var postOne = new Decorated();
+ postOne.set('adhoc', '9', Number);
+ postOne._path('adhoc').should.not.equal(undefined);
+
+ var postTwo = new Decorated();
+ postTwo._path('title').should.not.equal(undefined);
+ should.strictEqual(undefined, postTwo._path('adhoc'));
+ db.close();
+ },
+
+ 'querying a document that had an on the fly schema should work': function () {
+ var db = start()
+ , Decorated = db.model('Decorated', collection);
+
+ var post = new Decorated({title: 'AD HOC'});
+ // Interpret adhoc as a Number
+ post.set('adhoc', '9', Number);
+ post.get('adhoc').valueOf().should.eql(9);
+ post.save( function (err) {
+ should.strictEqual(null, err);
+ Decorated.findById(post.id, function (err, found) {
+ db.close();
+ should.strictEqual(null, err);
+ found.get('adhoc').should.eql(9);
+ // Interpret adhoc as a String instead of a Number now
+ found.get('adhoc', String).should.eql('9');
+ found.get('adhoc').should.eql('9');
+ });
+ });
+ },
+
+ 'on the fly Embedded Array schemas should cast properly': function () {
+ var db = start()
+ , Decorated = db.model('Decorated', collection);
+
+ var post = new Decorated();
+ post.set('moderators', [{name: 'alex trebek'}], [new Schema({name: String})]);
+ post.get('moderators')[0].name.should.eql('alex trebek');
+ db.close();
+ },
+
+ 'on the fly Embedded Array schemas should get from a fresh queried document properly': function () {
+ var db = start()
+ , Decorated = db.model('Decorated', collection);
+
+ var post = new Decorated()
+ , ModeratorSchema = new Schema({name: String, ranking: Number});
+ post.set('moderators', [{name: 'alex trebek', ranking: '1'}], [ModeratorSchema]);
+ post.get('moderators')[0].name.should.eql('alex trebek');
+ post.save( function (err) {
+ should.strictEqual(null, err);
+ Decorated.findById(post.id, function (err, found) {
+ db.close();
+ should.strictEqual(null, err);
+ var rankingPreCast = found.get('moderators')[0].ranking;
+ rankingPreCast.should.eql(1);
+ should.strictEqual(undefined, rankingPreCast.increment);
+ var rankingPostCast = found.get('moderators', [ModeratorSchema])[0].ranking;
+ rankingPostCast.valueOf().should.equal(1);
+ rankingPostCast.increment.should.not.equal(undefined);
+
+ var NewModeratorSchema = new Schema({ name: String, ranking: String});
+ rankingPostCast = found.get('moderators', [NewModeratorSchema])[0].ranking;
+ rankingPostCast.should.equal('1');
+ });
+ });
+ },
+ 'should support on the fly nested documents': function () {
+ // TODO
+ }
+};
diff --git a/node_modules/mongoose/test/schema.test.js b/node_modules/mongoose/test/schema.test.js
new file mode 100644
index 0000000..93a3545
--- /dev/null
+++ b/node_modules/mongoose/test/schema.test.js
@@ -0,0 +1,978 @@
+
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('./common').mongoose
+ , should = require('should')
+ , Schema = mongoose.Schema
+ , Document = mongoose.Document
+ , SchemaType = mongoose.SchemaType
+ , VirtualType = mongoose.VirtualType
+ , ObjectId = Schema.ObjectId
+ , ValidatorError = SchemaType.ValidatorError
+ , CastError = SchemaType.CastError
+ , SchemaTypes = Schema.Types
+ , DocumentObjectId = mongoose.Types.ObjectId
+ , Mixed = SchemaTypes.Mixed
+ , MongooseNumber = mongoose.Types.Number
+ , MongooseArray = mongoose.Types.Array
+ , vm = require('vm')
+
+/**
+ * Test Document constructor.
+ */
+
+function TestDocument () {
+ Document.apply(this, arguments);
+};
+
+/**
+ * Inherits from Document.
+ */
+
+TestDocument.prototype.__proto__ = Document.prototype;
+
+/**
+ * Set a dummy schema to simulate compilation.
+ */
+
+TestDocument.prototype.schema = new Schema({
+ test : String
+});
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test different schema types support': function(){
+ var Checkin = new Schema({
+ date : Date
+ , location : {
+ lat: Number
+ , lng: Number
+ }
+ });
+
+ var Ferret = new Schema({
+ name : String
+ , owner : ObjectId
+ , fur : String
+ , color : { type: String }
+ , age : Number
+ , checkins : [Checkin]
+ , friends : [ObjectId]
+ , likes : Array
+ , alive : Boolean
+ , extra : Mixed
+ });
+
+ Ferret.path('name').should.be.an.instanceof(SchemaTypes.String);
+ Ferret.path('owner').should.be.an.instanceof(SchemaTypes.ObjectId);
+ Ferret.path('fur').should.be.an.instanceof(SchemaTypes.String);
+ Ferret.path('color').should.be.an.instanceof(SchemaTypes.String);
+ Ferret.path('age').should.be.an.instanceof(SchemaTypes.Number);
+ Ferret.path('checkins').should.be.an.instanceof(SchemaTypes.DocumentArray);
+ Ferret.path('friends').should.be.an.instanceof(SchemaTypes.Array);
+ Ferret.path('likes').should.be.an.instanceof(SchemaTypes.Array);
+ Ferret.path('alive').should.be.an.instanceof(SchemaTypes.Boolean);
+ Ferret.path('extra').should.be.an.instanceof(SchemaTypes.Mixed);
+
+ should.strictEqual(Ferret.path('unexistent'), undefined);
+
+ Checkin.path('date').should.be.an.instanceof(SchemaTypes.Date);
+ },
+
+ 'dot notation support for accessing paths': function(){
+ var Racoon = new Schema({
+ name : { type: String, enum: ['Edwald', 'Tobi'] }
+ , age : Number
+ });
+
+ var Person = new Schema({
+ name : String
+ , raccoons : [Racoon]
+ , location : {
+ city : String
+ , state : String
+ }
+ });
+
+ Person.path('name').should.be.an.instanceof(SchemaTypes.String);
+ Person.path('raccoons').should.be.an.instanceof(SchemaTypes.DocumentArray);
+ Person.path('location.city').should.be.an.instanceof(SchemaTypes.String);
+ Person.path('location.state').should.be.an.instanceof(SchemaTypes.String);
+
+ should.strictEqual(Person.path('location.unexistent'), undefined);
+ },
+
+ 'nested paths more than 2 levels deep': function () {
+ var Nested = new Schema({
+ first: {
+ second: {
+ third: String
+ }
+ }
+ });
+ Nested.path('first.second.third').should.be.an.instanceof(SchemaTypes.String);
+ },
+
+ 'test default definition': function(){
+ var Test = new Schema({
+ simple : { type: String, default: 'a' }
+ , array : { type: Array, default: [1,2,3,4,5] }
+ , arrayX : { type: Array, default: 9 }
+ , arrayFn : { type: Array, default: function () { return [8] } }
+ , callback : { type: Number, default: function(){
+ this.a.should.eql('b');
+ return '3';
+ }}
+ });
+
+ Test.path('simple').defaultValue.should.eql('a');
+ Test.path('callback').defaultValue.should.be.a('function');
+
+ Test.path('simple').getDefault().should.eql('a');
+ (+Test.path('callback').getDefault({ a: 'b' })).should.eql(3);
+ Test.path('array').defaultValue.should.be.a('function');
+ Test.path('array').getDefault(new TestDocument)[3].should.eql(4);
+ Test.path('arrayX').getDefault(new TestDocument)[0].should.eql(9);
+ Test.path('arrayFn').defaultValue.should.be.a('function');
+ Test.path('arrayFn').getDefault(new TestDocument).should.be.an.instanceof(MongooseArray);
+ },
+
+ 'test Mixed defaults can be empty arrays': function () {
+ var Test = new Schema({
+ mixed1 : { type: Mixed, default: [] }
+ , mixed2 : { type: Mixed, default: Array }
+ });
+
+ Test.path('mixed1').getDefault().should.be.an.instanceof(Array);
+ Test.path('mixed1').getDefault().length.should.be.eql(0);
+ Test.path('mixed2').getDefault().should.be.an.instanceof(Array);
+ Test.path('mixed2').getDefault().length.should.be.eql(0);
+ },
+
+ 'test string required validation': function(){
+ var Test = new Schema({
+ simple: String
+ });
+
+ Test.path('simple').required(true);
+ Test.path('simple').validators.should.have.length(1);
+
+ Test.path('simple').doValidate(null, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('simple').doValidate(undefined, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('simple').doValidate('', function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('simple').doValidate('woot', function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test string enum validation': function(){
+ var Test = new Schema({
+ complex: { type: String, enum: ['a', 'b', undefined, 'c', null] }
+ });
+
+ Test.path('complex').should.be.an.instanceof(SchemaTypes.String);
+ Test.path('complex').enumValues.should.eql(['a', 'b', 'c', null]);
+ Test.path('complex').validators.should.have.length(1);
+
+ Test.path('complex').enum('d', 'e');
+
+ Test.path('complex').enumValues.should.eql(['a', 'b', 'c', null, 'd', 'e']);
+
+ Test.path('complex').doValidate('x', function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('complex').doValidate(undefined, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('complex').doValidate(null, function(err){
+ should.strictEqual(null, err);
+ });
+
+ Test.path('complex').doValidate('da', function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+ },
+
+ 'test string regular expression validation': function(){
+ var Test = new Schema({
+ simple: { type: String, match: /[a-z]/ }
+ });
+
+ Test.path('simple').validators.should.have.length(1);
+
+ Test.path('simple').doValidate('az', function(err){
+ should.strictEqual(err, null);
+ });
+
+ Test.path('simple').match(/[0-9]/);
+ Test.path('simple').validators.should.have.length(2);
+
+ Test.path('simple').doValidate('12', function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('simple').doValidate('a12', function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test string casting': function(){
+ var Tobi = new Schema({
+ nickname: String
+ });
+
+ function Test(){};
+ Test.prototype.toString = function(){
+ return 'woot';
+ };
+
+ // test Number -> String cast
+ Tobi.path('nickname').cast(0).should.be.a('string');
+ Tobi.path('nickname').cast(0).should.eql('0');
+
+ // test any object that implements toString
+ Tobi.path('nickname').cast(new Test()).should.be.a('string');
+ Tobi.path('nickname').cast(new Test()).should.eql('woot');
+ },
+
+ 'test number minimums and maximums validation': function(){
+ var Tobi = new Schema({
+ friends: { type: Number, max: 15, min: 5 }
+ });
+
+ Tobi.path('friends').validators.should.have.length(2);
+
+ Tobi.path('friends').doValidate(10, function(err){
+ should.strictEqual(err, null);
+ });
+
+ Tobi.path('friends').doValidate(100, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Tobi.path('friends').doValidate(1, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ // null is allowed
+ Tobi.path('friends').doValidate(null, function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test number required validation': function(){
+ var Edwald = new Schema({
+ friends: { type: Number, required: true }
+ });
+
+ Edwald.path('friends').doValidate(null, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Edwald.path('friends').doValidate(undefined, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Edwald.path('friends').doValidate(0, function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test number casting': function(){
+ var Tobi = new Schema({
+ age: Number
+ });
+
+ // test String -> Number cast
+ Tobi.path('age').cast('0').should.be.an.instanceof(MongooseNumber);
+ (+Tobi.path('age').cast('0')).should.eql(0);
+
+ Tobi.path('age').cast(0).should.be.an.instanceof(MongooseNumber);
+ (+Tobi.path('age').cast(0)).should.eql(0);
+ },
+
+ 'test date required validation': function(){
+ var Loki = new Schema({
+ birth_date: { type: Date, required: true }
+ });
+
+ Loki.path('birth_date').doValidate(null, function (err) {
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Loki.path('birth_date').doValidate(undefined, function (err) {
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Loki.path('birth_date').doValidate(new Date(), function (err) {
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test date casting': function(){
+ var Loki = new Schema({
+ birth_date: { type: Date }
+ });
+
+ Loki.path('birth_date').cast(1294525628301).should.be.an.instanceof(Date);
+ Loki.path('birth_date').cast('8/24/2000').should.be.an.instanceof(Date);
+ Loki.path('birth_date').cast(new Date).should.be.an.instanceof(Date);
+ },
+
+ 'test object id required validator': function(){
+ var Loki = new Schema({
+ owner: { type: ObjectId, required: true }
+ });
+
+ Loki.path('owner').doValidate(new DocumentObjectId(), function(err){
+ should.strictEqual(err, null);
+ });
+
+ Loki.path('owner').doValidate(null, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Loki.path('owner').doValidate(undefined, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+ },
+
+ 'test object id casting': function(){
+ var Loki = new Schema({
+ owner: { type: ObjectId }
+ });
+
+ var doc = new TestDocument()
+ , id = doc._id.toString();
+
+ Loki.path('owner').cast('4c54f3453e688c000000001a')
+ .should.be.an.instanceof(DocumentObjectId);
+
+ Loki.path('owner').cast(new DocumentObjectId())
+ .should.be.an.instanceof(DocumentObjectId);
+
+ Loki.path('owner').cast(doc)
+ .should.be.an.instanceof(DocumentObjectId);
+
+ Loki.path('owner').cast(doc).toString().should.eql(id);
+ },
+
+ 'test array required validation': function(){
+ var Loki = new Schema({
+ likes: { type: Array, required: true }
+ });
+
+ Loki.path('likes').doValidate(null, function (err) {
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Loki.path('likes').doValidate(undefined, function (err) {
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Loki.path('likes').doValidate([], function (err) {
+ err.should.be.an.instanceof(ValidatorError);
+ });
+ },
+
+ 'test array casting': function(){
+ var Loki = new Schema({
+ oids : [ObjectId]
+ , dates : [Date]
+ , numbers : [Number]
+ , strings : [String]
+ , buffers : [Buffer]
+ , nocast : []
+ , mixed : [Mixed]
+ });
+
+ var oids = Loki.path('oids').cast(['4c54f3453e688c000000001a', new DocumentObjectId]);
+
+ oids[0].should.be.an.instanceof(DocumentObjectId);
+ oids[1].should.be.an.instanceof(DocumentObjectId);
+
+ var dates = Loki.path('dates').cast(['8/24/2010', 1294541504958]);
+
+ dates[0].should.be.an.instanceof(Date);
+ dates[1].should.be.an.instanceof(Date);
+
+ var numbers = Loki.path('numbers').cast([152, '31']);
+
+ numbers[0].should.be.a('number');
+ numbers[1].should.be.a('number');
+
+ var strings = Loki.path('strings').cast(['test', 123]);
+
+ strings[0].should.be.a('string');
+ strings[0].should.eql('test');
+
+ strings[1].should.be.a('string');
+ strings[1].should.eql('123');
+
+ var buffers = Loki.path('buffers').cast(['\0\0\0', new Buffer("abc")]);
+
+ buffers[0].should.be.an.instanceof(Buffer);
+ buffers[1].should.be.an.instanceof(Buffer);
+
+ var nocasts = Loki.path('nocast').cast(['test', 123]);
+
+ nocasts[0].should.be.a('string');
+ nocasts[0].should.eql('test');
+
+ nocasts[1].should.be.a('number');
+ nocasts[1].should.eql(123);
+
+ var mixed = Loki.path('mixed').cast(['test', 123, '123', {}, new Date, new DocumentObjectId]);
+
+ mixed[0].should.be.a('string');
+ mixed[1].should.be.a('number');
+ mixed[2].should.be.a('string');
+ mixed[3].should.be.a('object');
+ mixed[4].should.be.an.instanceof(Date);
+ mixed[5].should.be.an.instanceof(DocumentObjectId);
+ },
+
+ 'test boolean required validator': function(){
+ var Animal = new Schema({
+ isFerret: { type: Boolean, required: true }
+ });
+
+ Animal.path('isFerret').doValidate(null, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Animal.path('isFerret').doValidate(undefined, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Animal.path('isFerret').doValidate(true, function(err){
+ should.strictEqual(err, null);
+ });
+
+ Animal.path('isFerret').doValidate(false, function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test boolean casting': function(){
+ var Animal = new Schema({
+ isFerret: { type: Boolean, required: true }
+ });
+
+ should.strictEqual(Animal.path('isFerret').cast(null), null);
+ Animal.path('isFerret').cast(undefined).should.be.false;
+ Animal.path('isFerret').cast(false).should.be.false;
+ Animal.path('isFerret').cast(0).should.be.false;
+ Animal.path('isFerret').cast('0').should.be.false;
+ Animal.path('isFerret').cast({}).should.be.true;
+ Animal.path('isFerret').cast(true).should.be.true;
+ Animal.path('isFerret').cast(1).should.be.true;
+ Animal.path('isFerret').cast('1').should.be.true;
+ },
+
+ 'test async multiple validators': function(beforeExit){
+ var executed = 0;
+
+ function validator (value, fn) {
+ setTimeout(function(){
+ executed++;
+ fn(value === true);
+ }, 50);
+ };
+
+ var Animal = new Schema({
+ ferret: {
+ type: Boolean,
+ validate: [
+ {
+ 'validator': validator,
+ 'msg': 'validator1'
+ },
+ {
+ 'validator': validator,
+ 'msg': 'validator2'
+ },
+ ],
+ }
+ });
+
+ Animal.path('ferret').doValidate(true, function(err){
+ should.strictEqual(err, null);
+ });
+
+ beforeExit(function(){
+ executed.should.eql(2);
+ });
+ },
+
+ 'test async validators': function(beforeExit){
+ var executed = 0;
+
+ function validator (value, fn) {
+ setTimeout(function(){
+ executed++;
+ fn(value === true);
+ }, 50);
+ };
+
+ var Animal = new Schema({
+ ferret: { type: Boolean, validate: validator }
+ });
+
+ Animal.path('ferret').doValidate(true, function(err){
+ should.strictEqual(err, null);
+ });
+
+ Animal.path('ferret').doValidate(false, function(err){
+ err.should.be.an.instanceof(Error);
+ });
+
+ beforeExit(function(){
+ executed.should.eql(2);
+ });
+ },
+
+ 'test async validators scope': function(beforeExit){
+ var executed = false;
+
+ function validator (value, fn) {
+ this.a.should.eql('b');
+
+ setTimeout(function(){
+ executed = true;
+ fn(true);
+ }, 50);
+ };
+
+ var Animal = new Schema({
+ ferret: { type: Boolean, validate: validator }
+ });
+
+ Animal.path('ferret').doValidate(true, function(err){
+ should.strictEqual(err, null);
+ }, { a: 'b' });
+
+ beforeExit(function(){
+ executed.should.be.true;
+ });
+ },
+
+ 'test declaring new methods': function(){
+ var a = new Schema();
+ a.method('test', function(){});
+ a.method({
+ a: function(){}
+ , b: function(){}
+ });
+
+ Object.keys(a.methods).should.have.length(3);
+ },
+
+ 'test declaring new statics': function(){
+ var a = new Schema();
+ a.static('test', function(){});
+ a.static({
+ a: function(){}
+ , b: function(){}
+ , c: function(){}
+ });
+
+ Object.keys(a.statics).should.have.length(4);
+ },
+
+ 'test setter(s)': function(){
+ function lowercase (v) {
+ return v.toLowerCase();
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, set: lowercase }
+ });
+
+ Tobi.path('name').applySetters('WOOT').should.eql('woot');
+ Tobi.path('name').setters.should.have.length(1);
+
+ Tobi.path('name').set(function(v){
+ return v + 'WOOT';
+ });
+
+ Tobi.path('name').applySetters('WOOT').should.eql('wootwoot');
+ Tobi.path('name').setters.should.have.length(2);
+ },
+
+ 'test setters scope': function(){
+ function lowercase (v, self) {
+ this.a.should.eql('b');
+ self.path.should.eql('name');
+ return v.toLowerCase();
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, set: lowercase }
+ });
+
+ Tobi.path('name').applySetters('WHAT', { a: 'b' }).should.eql('what');
+ },
+
+ 'test string built-in setter `lowercase`': function () {
+ var Tobi = new Schema({
+ name: { type: String, lowercase: true }
+ });
+
+ Tobi.path('name').applySetters('WHAT').should.eql('what');
+ },
+
+ 'test string built-in setter `uppercase`': function () {
+ var Tobi = new Schema({
+ name: { type: String, uppercase: true }
+ });
+
+ Tobi.path('name').applySetters('what').should.eql('WHAT');
+ },
+
+ 'test string built-in setter `trim`': function () {
+ var Tobi = new Schema({
+ name: { type: String, uppercase: true, trim: true }
+ });
+
+ Tobi.path('name').applySetters(' what ').should.eql('WHAT');
+ },
+
+ 'test getter(s)': function(){
+ function woot (v) {
+ return v + ' woot';
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, get: woot }
+ });
+
+ Tobi.path('name').getters.should.have.length(1);
+ Tobi.path('name').applyGetters('test').should.eql('test woot');
+ },
+
+ 'test getters scope': function(){
+ function woot (v, self) {
+ this.a.should.eql('b');
+ self.path.should.eql('name');
+ return v.toLowerCase();
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, get: woot }
+ });
+
+ Tobi.path('name').applyGetters('YEP', { a: 'b' }).should.eql('yep');
+ },
+
+ 'test setters casting': function(){
+ function last (v) {
+ v.should.be.a('string');
+ v.should.eql('0');
+ return 'last';
+ };
+
+ function first (v) {
+ return 0;
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, set: last }
+ });
+
+ Tobi.path('name').set(first);
+ Tobi.path('name').applySetters('woot').should.eql('last');
+ },
+
+ 'test getters casting': function(){
+ function last (v) {
+ v.should.be.a('string');
+ v.should.eql('0');
+ return 'last';
+ };
+
+ function first (v) {
+ return 0;
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, get: last }
+ });
+
+ Tobi.path('name').get(first);
+ Tobi.path('name').applyGetters('woot').should.eql('last');
+ },
+
+ 'test hooks registration': function(){
+ var Tobi = new Schema();
+
+ Tobi.pre('save', function(){});
+ Tobi.callQueue.should.have.length(1);
+
+ Tobi.post('save', function(){});
+ Tobi.callQueue.should.have.length(2);
+
+ Tobi.pre('save', function(){});
+ Tobi.callQueue.should.have.length(3);
+ },
+
+ 'test applying setters when none have been defined': function(){
+ var Tobi = new Schema({
+ name: String
+ });
+
+ Tobi.path('name').applySetters('woot').should.eql('woot');
+ },
+
+ 'test applying getters when none have been defined': function(){
+ var Tobi = new Schema({
+ name: String
+ });
+
+ Tobi.path('name').applyGetters('woot').should.eql('woot');
+ },
+
+ 'test defining an index': function(){
+ var Tobi = new Schema({
+ name: { type: String, index: true }
+ });
+
+ Tobi.path('name')._index.should.be.true;
+ Tobi.path('name').index({ unique: true });
+ Tobi.path('name')._index.should.eql({ unique: true });
+ Tobi.path('name').unique(false);
+ Tobi.path('name')._index.should.eql({ unique: false});
+
+ var T1 = new Schema({
+ name: { type: String, sparse: true }
+ });
+ T1.path('name')._index.should.eql({ sparse: true });
+
+ var T2 = new Schema({
+ name: { type: String, unique: true }
+ });
+ T2.path('name')._index.should.eql({ unique: true });
+
+ var T3 = new Schema({
+ name: { type: String, sparse: true, unique: true }
+ });
+ T3.path('name')._index.should.eql({ sparse: true, unique: true });
+
+ var T4 = new Schema({
+ name: { type: String, unique: true, sparse: true }
+ });
+ var i = T4.path('name')._index;
+ i.unique.should.be.true;
+ i.sparse.should.be.true;
+
+ var T5 = new Schema({
+ name: { type: String, index: { sparse: true, unique: true } }
+ });
+ var i = T5.path('name')._index;
+ i.unique.should.be.true;
+ i.sparse.should.be.true;
+ },
+
+ 'test defining compound indexes': function(){
+ var Tobi = new Schema({
+ name: { type: String, index: true }
+ , last: { type: Number, sparse: true }
+ });
+
+ Tobi.index({ firstname: 1, last: 1 }, { unique: true });
+
+ Tobi.indexes.should.eql([
+ [{ name: 1 }, {}]
+ , [{ last: 1 }, { sparse: true }]
+ , [{ firstname: 1, last: 1}, {unique: true}]
+ ]);
+ },
+
+ 'test plugins': function (beforeExit) {
+ var Tobi = new Schema()
+ , called = false;
+
+ Tobi.plugin(function(schema){
+ schema.should.equal(Tobi);
+ called = true;
+ });
+
+ beforeExit(function () {
+ called.should.be.true;
+ });
+ },
+
+ 'test that default options are set': function () {
+ var Tobi = new Schema();
+
+ Tobi.options.should.be.a('object');
+ Tobi.options.safe.should.be.true;
+ },
+
+ 'test setting options': function () {
+ var Tobi = new Schema({}, { collection: 'users' });
+
+ Tobi.set('a', 'b');
+ Tobi.set('safe', false);
+ Tobi.options.collection.should.eql('users');
+
+ Tobi.options.a.should.eql('b');
+ Tobi.options.safe.should.be.false;
+ },
+
+ 'test declaring virtual attributes': function () {
+ var Contact = new Schema({
+ firstName: String
+ , lastName: String
+ });
+ Contact.virtual('fullName')
+ .get( function () {
+ return this.get('firstName') + ' ' + this.get('lastName');
+ }).set(function (fullName) {
+ var split = fullName.split(' ');
+ this.set('firstName', split[0]);
+ this.set('lastName', split[1]);
+ });
+
+ Contact.virtualpath('fullName').should.be.an.instanceof(VirtualType);
+ },
+
+ 'test GH-298 - The default creation of a virtual `id` should be muted when someone defines their own `id` attribute': function () {
+ new Schema({ id: String });
+ },
+
+ 'allow disabling the auto .id virtual': function () {
+ var schema = new Schema({ name: String }, { noVirtualId: true });
+ should.strictEqual(undefined, schema.virtuals.id);
+ },
+
+ 'selected option': function () {
+ var s = new Schema({ thought: { type: String, select: false }});
+ s.path('thought').selected.should.be.false;
+
+ var a = new Schema({ thought: { type: String, select: true }});
+ a.path('thought').selected.should.be.true;
+ },
+
+ 'schema creation works with objects from other contexts': function () {
+ var str = 'code = {' +
+ ' name: String' +
+ ', arr1: Array ' +
+ ', arr2: { type: [] }' +
+ ', date: Date ' +
+ ', num: { type: Number }' +
+ ', bool: Boolean' +
+ ', nest: { sub: { type: {}, required: true }}' +
+ '}';
+
+ var script = vm.createScript(str, 'testSchema.vm');
+ var sandbox = { code: null };
+ script.runInNewContext(sandbox);
+
+ var Ferret = new Schema(sandbox.code);
+ Ferret.path('nest.sub').should.be.an.instanceof(SchemaTypes.Mixed);
+ Ferret.path('name').should.be.an.instanceof(SchemaTypes.String);
+ Ferret.path('arr1').should.be.an.instanceof(SchemaTypes.Array);
+ Ferret.path('arr2').should.be.an.instanceof(SchemaTypes.Array);
+ Ferret.path('date').should.be.an.instanceof(SchemaTypes.Date);
+ Ferret.path('num').should.be.an.instanceof(SchemaTypes.Number);
+ Ferret.path('bool').should.be.an.instanceof(SchemaTypes.Boolean);
+ },
+
+ 'schema string casts undefined to "undefined"': function () {
+ var db= require('./common')();
+ var schema = new Schema({ arr: [String] });
+ var M = db.model('castingStringArrayWithUndefined', schema);
+ M.find({ arr: { $in: [undefined] }}, function (err) {
+ db.close();
+ should.equal(err && err.message, 'Cast to string failed for value "undefined"');
+ })
+ },
+
+ 'array of object literal missing a `type` is interpreted as Mixed': function () {
+ var s = new Schema({
+ arr: [
+ { something: { type: String } }
+ ]
+ });
+ },
+
+ 'helpful schema debugging msg': function () {
+ var err;
+ try {
+ new Schema({ name: { first: null } })
+ } catch (e) {
+ err = e;
+ }
+ err.message.should.equal('Invalid value for schema path `name.first`')
+ try {
+ new Schema({ age: undefined })
+ } catch (e) {
+ err = e;
+ }
+ err.message.should.equal('Invalid value for schema path `age`')
+ },
+
+ 'add() does not polute existing paths': function () {
+ var o = { name: String }
+ var s = new Schema(o);
+ s.add({ age: Number }, 'name.');
+ ;('age' in o.name).should.be.false;
+ },
+
+ // gh-700
+ 'nested schemas should throw': function () {
+ var a = new Schema({ title: String })
+ , err
+
+ try {
+ new Schema({ blah: Boolean, a: a });
+ } catch (err_) {
+ err = err_;
+ }
+
+ should.exist(err);
+ ;/Did you try nesting Schemas/.test(err.message).should.be.true;
+ },
+
+ 'non-function etters throw': function () {
+ var schema = new Schema({ fun: String });
+ var g, s;
+
+ try {
+ schema.path('fun').get(true);
+ } catch (err_) {
+ g = err_;
+ }
+
+ should.exist(g);
+ g.message.should.equal('A getter must be a function.');
+
+ try {
+ schema.path('fun').set(4);
+ } catch (err_) {
+ s = err_;
+ }
+
+ should.exist(s);
+ s.message.should.equal('A setter must be a function.');
+ }
+
+};
diff --git a/node_modules/mongoose/test/shard.test.js b/node_modules/mongoose/test/shard.test.js
new file mode 100644
index 0000000..2df04c3
--- /dev/null
+++ b/node_modules/mongoose/test/shard.test.js
@@ -0,0 +1,216 @@
+
+var start = require('./common')
+ , should = require('should')
+ , random = require('../lib/utils').random
+ , mongoose = start.mongoose
+ , Mongoose = mongoose.Mongoose
+ , Schema = mongoose.Schema;
+
+var uri = process.env.MONGOOSE_SHARD_TEST_URI;
+
+if (!uri) {
+ console.log('\033[30m', '\n', 'You\'re not testing shards!'
+ , '\n', 'Please set the MONGOOSE_SHARD_TEST_URI env variable.', '\n'
+ , 'e.g: `mongodb://localhost:27017/database', '\n'
+ , 'Sharding must already be enabled on your database'
+ , '\033[39m');
+
+ exports.r = function expressoHack(){}
+ return;
+}
+
+var schema = new Schema({
+ name: String
+ , age: Number
+ , likes: [String]
+}, { shardkey: { name: 1, age: 1 }});
+
+var collection = 'shardperson_' + random();
+mongoose.model('ShardPerson', schema, collection);
+
+var db = start({ uri: uri });
+db.on('open', function () {
+
+ // set up a sharded test collection
+ var P = db.model('ShardPerson', collection);
+
+ var cmd = {};
+ cmd.shardcollection = db.name + '.' + collection;
+ cmd.key = P.schema.options.shardkey;
+
+ P.db.db.executeDbAdminCommand(cmd,function (err, res) {
+ db.close();
+
+ if (err) throw err;
+
+ if (!(res && res.documents && res.documents[0] && res.documents[0].ok)) {
+ throw new Error('could not shard test collection ' + collection);
+ }
+
+ // assign exports to tell expresso to begin
+ Object.keys(tests).forEach(function (test) {
+ exports[test] = tests[test];
+ });
+
+ });
+});
+
+var tests = {
+
+ 'can read and write to a shard': function () {
+ var db = start({ uri: uri })
+ var P = db.model('ShardPerson', collection);
+
+ P.create({ name: 'ryu', age: 25, likes: ['street fighting']}, function (err, ryu) {
+ should.strictEqual(null, err);
+ P.findById(ryu._id, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.id.should.equal(ryu.id);
+ });
+ });
+ },
+
+ 'save() and remove() works with shard keys transparently': function () {
+ var db = start({ uri: uri })
+ var P = db.model('ShardPerson', collection);
+
+ var zangief = new P({ name: 'Zangief', age: 33 });
+ zangief.save(function (err) {
+ should.strictEqual(null, err);
+
+ zangief._shardval.name.should.equal('Zangief');
+ zangief._shardval.age.should.equal(33);
+
+ P.findById(zangief._id, function (err, zang) {
+ should.strictEqual(null, err);
+
+ zang._shardval.name.should.equal('Zangief');
+ zang._shardval.age.should.equal(33);
+
+ zang.likes = ['spinning', 'laughing'];
+ zang.save(function (err) {
+ should.strictEqual(null, err);
+
+ zang._shardval.name.should.equal('Zangief');
+ zang._shardval.age.should.equal(33);
+
+ zang.likes.addToSet('winning');
+ zang.save(function (err) {
+ should.strictEqual(null, err);
+ zang._shardval.name.should.equal('Zangief');
+ zang._shardval.age.should.equal(33);
+ zang.remove(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'inserting to a sharded collection without the full shard key fails': function () {
+ var db = start({ uri: uri })
+ var P = db.model('ShardPerson', collection);
+
+ var pending = 6;
+
+ P.create({ name: 'ryu', likes: ['street fighting']}, function (err, ryu) {
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ P.create({ likes: ['street fighting']}, function (err, ryu) { should.exist(err);
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ P.create({ name: 'ryu' }, function (err, ryu) { should.exist(err);
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ P.create({ age: 49 }, function (err, ryu) { should.exist(err);
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ P.create({ likes: ['street fighting'], age: 8 }, function (err, ryu) {
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ var p = new P;
+ p.save(function (err) {
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ },
+
+ 'updating a sharded collection without the full shard key fails': function () {
+ var db = start({ uri: uri })
+ var P = db.model('ShardPerson', collection);
+
+ P.create({ name: 'ken', age: 27 }, function (err, ken) {
+ should.strictEqual(null, err);
+
+ P.update({ _id: ken._id }, { likes: ['kicking', 'punching'] }, function (err) {
+ should.exist(err);
+ "right object doesn't have full shard key".should.equal(err.message);
+
+ P.update({ _id: ken._id, name: 'ken' }, { likes: ['kicking', 'punching'] }, function (err) {
+ should.exist(err);
+
+ P.update({ _id: ken._id, age: 27 }, { likes: ['kicking', 'punching'] }, function (err) {
+ should.exist(err);
+
+ P.update({ age: 27 }, { likes: ['kicking', 'punching'] }, function (err) {
+ db.close();
+ should.exist(err);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'updating shard key values fails': function () {
+ var db = start({ uri: uri })
+ var P = db.model('ShardPerson', collection);
+ P.create({ name: 'chun li', age: 19, likes: ['street fighting']}, function (err, chunli) {
+ should.strictEqual(null, err);
+
+ chunli._shardval.name.should.equal('chun li');
+ chunli._shardval.age.should.equal(19);
+
+ chunli.age = 20;
+ chunli.save(function (err) {
+ /^Can't modify shard key's value field/.test(err.message).should.be.true;
+
+ chunli._shardval.name.should.equal('chun li');
+ chunli._shardval.age.should.equal(19);
+
+ P.findById(chunli._id, function (err, chunli) {
+ should.strictEqual(null, err);
+
+ chunli._shardval.name.should.equal('chun li');
+ chunli._shardval.age.should.equal(19);
+
+ chunli.name='chuuuun liiiii';
+ chunli.save(function (err) {
+ db.close();
+ /^Can't modify shard key's value field/.test(err.message).should.be.true;
+ });
+ });
+ });
+ });
+ }
+}
diff --git a/node_modules/mongoose/test/types.array.test.js b/node_modules/mongoose/test/types.array.test.js
new file mode 100644
index 0000000..8a37399
--- /dev/null
+++ b/node_modules/mongoose/test/types.array.test.js
@@ -0,0 +1,551 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = require('./common').mongoose
+ , Schema = mongoose.Schema
+ , random = require('../lib/utils').random
+ , MongooseArray = mongoose.Types.Array;
+
+var User = new Schema({
+ name: String
+ , pets: [Schema.ObjectId]
+});
+
+mongoose.model('User', User);
+
+var Pet = new Schema({
+ name: String
+});
+
+mongoose.model('Pet', Pet);
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that a mongoose array behaves and quacks like an array': function(){
+ var a = new MongooseArray;
+
+ a.should.be.an.instanceof(Array);
+ a.should.be.an.instanceof(MongooseArray);
+ Array.isArray(a).should.be.true;
+ ;(a._atomics.constructor).should.eql(Object);
+
+ },
+
+ 'doAtomics does not throw': function () {
+ var b = new MongooseArray([12,3,4,5]).filter(Boolean);
+ var threw = false;
+
+ try {
+ b.doAtomics
+ } catch (_) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ var a = new MongooseArray([67,8]).filter(Boolean);
+ try {
+ a.push(3,4);
+ } catch (_) {
+ console.error(_);
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ },
+
+ 'test indexOf()': function(){
+ var db = start()
+ , User = db.model('User', 'users_' + random())
+ , Pet = db.model('Pet', 'pets' + random());
+
+ var tj = new User({ name: 'tj' })
+ , tobi = new Pet({ name: 'tobi' })
+ , loki = new Pet({ name: 'loki' })
+ , jane = new Pet({ name: 'jane' })
+ , pets = [];
+
+ tj.pets.push(tobi);
+ tj.pets.push(loki);
+ tj.pets.push(jane);
+
+ var pending = 3;
+
+ ;[tobi, loki, jane].forEach(function(pet){
+ pet.save(function(){
+ --pending || done();
+ });
+ });
+
+ function done() {
+ Pet.find({}, function(err, pets){
+ tj.save(function(err){
+ User.findOne({ name: 'tj' }, function(err, user){
+ db.close();
+ should.equal(null, err, 'error in callback');
+ user.pets.should.have.length(3);
+ user.pets.indexOf(tobi.id).should.equal(0);
+ user.pets.indexOf(loki.id).should.equal(1);
+ user.pets.indexOf(jane.id).should.equal(2);
+ user.pets.indexOf(tobi._id).should.equal(0);
+ user.pets.indexOf(loki._id).should.equal(1);
+ user.pets.indexOf(jane._id).should.equal(2);
+ });
+ });
+ });
+ }
+ },
+
+ 'test #splice() with numbers': function () {
+ var collection = 'splicetest-number' + random();
+ var db = start()
+ , schema = new Schema({ numbers: Array })
+ , A = db.model('splicetestNumber', schema, collection);
+
+ var a = new A({ numbers: [4,5,6,7] });
+ a.save(function (err) {
+ should.equal(null, err, 'could not save splice test');
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err, 'error finding splice doc');
+ var removed = doc.numbers.splice(1, 1);
+ removed.should.eql([5]);
+ doc.numbers.toObject().should.eql([4,6,7]);
+ doc.save(function (err) {
+ should.equal(null, err, 'could not save splice test');
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err, 'error finding splice doc');
+ doc.numbers.toObject().should.eql([4,6,7]);
+
+ A.collection.drop(function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test #splice() on embedded docs': function () {
+ var collection = 'splicetest-embeddeddocs' + random();
+ var db = start()
+ , schema = new Schema({ types: [new Schema({ type: String }) ]})
+ , A = db.model('splicetestEmbeddedDoc', schema, collection);
+
+ var a = new A({ types: [{type:'bird'},{type:'boy'},{type:'frog'},{type:'cloud'}] });
+ a.save(function (err) {
+ should.equal(null, err, 'could not save splice test');
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err, 'error finding splice doc');
+
+ var removed = doc.types.splice(1, 1);
+ removed.length.should.eql(1);
+ removed[0].type.should.eql('boy');
+
+ var obj = doc.types.toObject();
+ obj[0].type.should.eql('bird');
+ obj[1].type.should.eql('frog');
+ obj[2].type.should.eql('cloud');
+
+ doc.save(function (err) {
+ should.equal(null, err, 'could not save splice test');
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err, 'error finding splice doc');
+
+ var obj = doc.types.toObject();
+ obj[0].type.should.eql('bird');
+ obj[1].type.should.eql('frog');
+ obj[2].type.should.eql('cloud');
+
+ A.collection.drop(function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ '#unshift': function () {
+ var db = start()
+ , schema = new Schema({
+ types: [new Schema({ type: String })]
+ , nums: [Number]
+ , strs: [String]
+ })
+ , A = db.model('unshift', schema, 'unshift'+random());
+
+ var a = new A({
+ types: [{type:'bird'},{type:'boy'},{type:'frog'},{type:'cloud'}]
+ , nums: [1,2,3]
+ , strs: 'one two three'.split(' ')
+ });
+
+ a.save(function (err) {
+ should.equal(null, err);
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err);
+
+ var tlen = doc.types.unshift({type:'tree'});
+ var nlen = doc.nums.unshift(0);
+ var slen = doc.strs.unshift('zero');
+
+ tlen.should.equal(5);
+ nlen.should.equal(4);
+ slen.should.equal(4);
+
+ var obj = doc.types.toObject();
+ obj[0].type.should.eql('tree');
+ obj[1].type.should.eql('bird');
+ obj[2].type.should.eql('boy');
+ obj[3].type.should.eql('frog');
+ obj[4].type.should.eql('cloud');
+
+ obj = doc.nums.toObject();
+ obj[0].valueOf().should.equal(0);
+ obj[1].valueOf().should.equal(1);
+ obj[2].valueOf().should.equal(2);
+ obj[3].valueOf().should.equal(3);
+
+ obj = doc.strs.toObject();
+ obj[0].should.equal('zero');
+ obj[1].should.equal('one');
+ obj[2].should.equal('two');
+ obj[3].should.equal('three');
+
+ doc.save(function (err) {
+ should.equal(null, err);
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err);
+
+ var obj = doc.types.toObject();
+ obj[0].type.should.eql('tree');
+ obj[1].type.should.eql('bird');
+ obj[2].type.should.eql('boy');
+ obj[3].type.should.eql('frog');
+ obj[4].type.should.eql('cloud');
+
+ obj = doc.nums.toObject();
+ obj[0].valueOf().should.equal(0);
+ obj[1].valueOf().should.equal(1);
+ obj[2].valueOf().should.equal(2);
+ obj[3].valueOf().should.equal(3);
+
+ obj = doc.strs.toObject();
+ obj[0].should.equal('zero');
+ obj[1].should.equal('one');
+ obj[2].should.equal('two');
+ obj[3].should.equal('three');
+
+ A.collection.drop(function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ '#addToSet': function () {
+ var db = start()
+ , e = new Schema({ name: String, arr: [] })
+ , schema = new Schema({
+ num: [Number]
+ , str: [String]
+ , doc: [e]
+ , date: [Date]
+ , id: [Schema.ObjectId]
+ });
+
+ var M = db.model('testAddToSet', schema);
+ var m = new M;
+
+ m.num.push(1,2,3);
+ m.str.push('one','two','tres');
+ m.doc.push({ name: 'Dubstep', arr: [1] }, { name: 'Polka', arr: [{ x: 3 }]});
+
+ var d1 = new Date;
+ var d2 = new Date( +d1 + 60000);
+ var d3 = new Date( +d1 + 30000);
+ var d4 = new Date( +d1 + 20000);
+ var d5 = new Date( +d1 + 90000);
+ var d6 = new Date( +d1 + 10000);
+ m.date.push(d1, d2);
+
+ var id1 = new mongoose.Types.ObjectId;
+ var id2 = new mongoose.Types.ObjectId;
+ var id3 = new mongoose.Types.ObjectId;
+ var id4 = new mongoose.Types.ObjectId;
+ var id5 = new mongoose.Types.ObjectId;
+ var id6 = new mongoose.Types.ObjectId;
+
+ m.id.push(id1, id2);
+
+ m.num.addToSet(3,4,5);
+ m.num.length.should.equal(5);
+ m.str.$addToSet('four', 'five', 'two');
+ m.str.length.should.equal(5);
+ m.id.addToSet(id2, id3);
+ m.id.length.should.equal(3);
+ m.doc.$addToSet(m.doc[0]);
+ m.doc.length.should.equal(2);
+ m.doc.$addToSet({ name: 'Waltz', arr: [1] }, m.doc[0]);
+ m.doc.length.should.equal(3);
+ m.date.length.should.equal(2);
+ m.date.$addToSet(d1);
+ m.date.length.should.equal(2);
+ m.date.addToSet(d3);
+ m.date.length.should.equal(3);
+
+ m.save(function (err) {
+ should.strictEqual(null, err);
+ M.findById(m, function (err, m) {
+ should.strictEqual(null, err);
+
+ m.num.length.should.equal(5);
+ (~m.num.indexOf(1)).should.be.ok;
+ (~m.num.indexOf(2)).should.be.ok;
+ (~m.num.indexOf(3)).should.be.ok;
+ (~m.num.indexOf(4)).should.be.ok;
+ (~m.num.indexOf(5)).should.be.ok;
+
+ m.str.length.should.equal(5);
+ (~m.str.indexOf('one')).should.be.ok;
+ (~m.str.indexOf('two')).should.be.ok;
+ (~m.str.indexOf('tres')).should.be.ok;
+ (~m.str.indexOf('four')).should.be.ok;
+ (~m.str.indexOf('five')).should.be.ok;
+
+ m.id.length.should.equal(3);
+ (~m.id.indexOf(id1)).should.be.ok;
+ (~m.id.indexOf(id2)).should.be.ok;
+ (~m.id.indexOf(id3)).should.be.ok;
+
+ m.date.length.should.equal(3);
+ (~m.date.indexOf(d1.toString())).should.be.ok;
+ (~m.date.indexOf(d2.toString())).should.be.ok;
+ (~m.date.indexOf(d3.toString())).should.be.ok;
+
+ m.doc.length.should.equal(3);
+ m.doc.some(function(v){return v.name === 'Waltz'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Dubstep'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Polka'}).should.be.ok
+
+ // test single $addToSet
+ m.num.addToSet(3,4,5,6);
+ m.num.length.should.equal(6);
+ m.str.$addToSet('four', 'five', 'two', 'six');
+ m.str.length.should.equal(6);
+ m.id.addToSet(id2, id3, id4);
+ m.id.length.should.equal(4);
+
+ m.date.$addToSet(d1, d3, d4);
+ m.date.length.should.equal(4);
+
+ m.doc.$addToSet(m.doc[0], { name: '8bit' });
+ m.doc.length.should.equal(4);
+
+ m.save(function (err) {
+ should.strictEqual(null, err);
+
+ M.findById(m, function (err, m) {
+ should.strictEqual(null, err);
+
+ m.num.length.should.equal(6);
+ (~m.num.indexOf(1)).should.be.ok;
+ (~m.num.indexOf(2)).should.be.ok;
+ (~m.num.indexOf(3)).should.be.ok;
+ (~m.num.indexOf(4)).should.be.ok;
+ (~m.num.indexOf(5)).should.be.ok;
+ (~m.num.indexOf(6)).should.be.ok;
+
+ m.str.length.should.equal(6);
+ (~m.str.indexOf('one')).should.be.ok;
+ (~m.str.indexOf('two')).should.be.ok;
+ (~m.str.indexOf('tres')).should.be.ok;
+ (~m.str.indexOf('four')).should.be.ok;
+ (~m.str.indexOf('five')).should.be.ok;
+ (~m.str.indexOf('six')).should.be.ok;
+
+ m.id.length.should.equal(4);
+ (~m.id.indexOf(id1)).should.be.ok;
+ (~m.id.indexOf(id2)).should.be.ok;
+ (~m.id.indexOf(id3)).should.be.ok;
+ (~m.id.indexOf(id4)).should.be.ok;
+
+ m.date.length.should.equal(4);
+ (~m.date.indexOf(d1.toString())).should.be.ok;
+ (~m.date.indexOf(d2.toString())).should.be.ok;
+ (~m.date.indexOf(d3.toString())).should.be.ok;
+ (~m.date.indexOf(d4.toString())).should.be.ok;
+
+ m.doc.length.should.equal(4);
+ m.doc.some(function(v){return v.name === 'Waltz'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Dubstep'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Polka'}).should.be.ok
+ m.doc.some(function(v){return v.name === '8bit'}).should.be.ok
+
+ // test multiple $addToSet
+ m.num.addToSet(7,8);
+ m.num.length.should.equal(8);
+ m.str.$addToSet('seven', 'eight');
+ m.str.length.should.equal(8);
+ m.id.addToSet(id5, id6);
+ m.id.length.should.equal(6);
+
+ m.date.$addToSet(d5, d6);
+ m.date.length.should.equal(6);
+
+ m.doc.$addToSet(m.doc[1], { name: 'BigBeat' }, { name: 'Funk' });
+ m.doc.length.should.equal(6);
+
+ m.save(function (err) {
+ should.strictEqual(null, err);
+
+ M.findById(m, function (err, m) {
+ db.close();
+ should.strictEqual(null, err);
+
+ m.num.length.should.equal(8);
+ (~m.num.indexOf(1)).should.be.ok;
+ (~m.num.indexOf(2)).should.be.ok;
+ (~m.num.indexOf(3)).should.be.ok;
+ (~m.num.indexOf(4)).should.be.ok;
+ (~m.num.indexOf(5)).should.be.ok;
+ (~m.num.indexOf(6)).should.be.ok;
+ (~m.num.indexOf(7)).should.be.ok;
+ (~m.num.indexOf(8)).should.be.ok;
+
+ m.str.length.should.equal(8);
+ (~m.str.indexOf('one')).should.be.ok;
+ (~m.str.indexOf('two')).should.be.ok;
+ (~m.str.indexOf('tres')).should.be.ok;
+ (~m.str.indexOf('four')).should.be.ok;
+ (~m.str.indexOf('five')).should.be.ok;
+ (~m.str.indexOf('six')).should.be.ok;
+ (~m.str.indexOf('seven')).should.be.ok;
+ (~m.str.indexOf('eight')).should.be.ok;
+
+ m.id.length.should.equal(6);
+ (~m.id.indexOf(id1)).should.be.ok;
+ (~m.id.indexOf(id2)).should.be.ok;
+ (~m.id.indexOf(id3)).should.be.ok;
+ (~m.id.indexOf(id4)).should.be.ok;
+ (~m.id.indexOf(id5)).should.be.ok;
+ (~m.id.indexOf(id6)).should.be.ok;
+
+ m.date.length.should.equal(6);
+ (~m.date.indexOf(d1.toString())).should.be.ok;
+ (~m.date.indexOf(d2.toString())).should.be.ok;
+ (~m.date.indexOf(d3.toString())).should.be.ok;
+ (~m.date.indexOf(d4.toString())).should.be.ok;
+ (~m.date.indexOf(d5.toString())).should.be.ok;
+ (~m.date.indexOf(d6.toString())).should.be.ok;
+
+ m.doc.length.should.equal(6);
+ m.doc.some(function(v){return v.name === 'Waltz'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Dubstep'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Polka'}).should.be.ok
+ m.doc.some(function(v){return v.name === '8bit'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'BigBeat'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Funk'}).should.be.ok
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'setting doc array should adjust path positions': function () {
+ var db = start();
+
+ var D = db.model('subDocPositions', new Schema({
+ em1: [new Schema({ name: String })]
+ }));
+
+ var d = new D({
+ em1: [
+ { name: 'pos0' }
+ , { name: 'pos1' }
+ , { name: 'pos2' }
+ ]
+ });
+
+ d.save(function (err) {
+ should.strictEqual(null, err);
+ D.findById(d, function (err, d) {
+ should.strictEqual(null, err);
+
+ var n = d.em1.slice();
+ n[2].name = 'position two';
+ var x = [];
+ x[1] = n[2];
+ x[2] = n[1];
+ d.em1 = x.filter(Boolean);
+
+ d.save(function (err) {
+ should.strictEqual(null, err);
+ D.findById(d, function (err, d) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ });
+ },
+
+ 'paths with similar names should be saved': function () {
+ var db = start();
+
+ var D = db.model('similarPathNames', new Schema({
+ account: {
+ role: String
+ , roles: [String]
+ }
+ , em: [new Schema({ name: String })]
+ }));
+
+ var d = new D({
+ account: { role: 'teacher', roles: ['teacher', 'admin'] }
+ , em: [{ name: 'bob' }]
+ });
+
+ d.save(function (err) {
+ should.strictEqual(null, err);
+ D.findById(d, function (err, d) {
+ should.strictEqual(null, err);
+
+ d.account.role = 'president';
+ d.account.roles = ['president', 'janitor'];
+ d.em[0].name = 'memorable';
+ d.em = [{ name: 'frida' }];
+
+ d.save(function (err) {
+ should.strictEqual(null, err);
+ D.findById(d, function (err, d) {
+ db.close();
+ should.strictEqual(null, err);
+ d.account.role.should.equal('president');
+ d.account.roles.length.should.equal(2);
+ d.account.roles[0].should.equal('president');
+ d.account.roles[1].should.equal('janitor');
+ d.em.length.should.equal(1);
+ d.em[0].name.should.equal('frida');
+ });
+ });
+ });
+ });
+ }
+};
diff --git a/node_modules/mongoose/test/types.buffer.test.js b/node_modules/mongoose/test/types.buffer.test.js
new file mode 100644
index 0000000..b9c8d5d
--- /dev/null
+++ b/node_modules/mongoose/test/types.buffer.test.js
@@ -0,0 +1,350 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = require('./common').mongoose
+ , Schema = mongoose.Schema
+ , random = require('../lib/utils').random
+ , MongooseBuffer = mongoose.Types.Buffer;
+
+// can't index Buffer fields yet
+
+function valid (v) {
+ return !v || v.length > 10;
+}
+
+var subBuf = new Schema({
+ name: String
+ , buf: { type: Buffer, validate: [valid, 'valid failed'], required: true }
+});
+
+var UserBuffer = new Schema({
+ name: String
+ , serial: Buffer
+ , array: [Buffer]
+ , required: { type: Buffer, required: true, index: true }
+ , sub: [subBuf]
+});
+
+// Dont put indexed models on the default connection, it
+// breaks index.test.js tests on a "pure" default conn.
+// mongoose.model('UserBuffer', UserBuffer);
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that a mongoose buffer behaves and quacks like an buffer': function(){
+ var a = new MongooseBuffer;
+
+ a.should.be.an.instanceof(Buffer);
+ a.should.be.an.instanceof(MongooseBuffer);
+ Buffer.isBuffer(a).should.be.true;
+
+ var a = new MongooseBuffer([195, 188, 98, 101, 114]);
+ var b = new MongooseBuffer("buffer shtuffs are neat");
+ var c = new MongooseBuffer('aGVsbG8gd29ybGQ=', 'base64');
+
+ a.toString('utf8').should.equal('über');
+ b.toString('utf8').should.equal('buffer shtuffs are neat');
+ c.toString('utf8').should.equal('hello world');
+ },
+
+ 'buffer validation': function () {
+ var db = start()
+ , User = db.model('UserBuffer', UserBuffer, 'usersbuffer_' + random());
+
+ User.on('index', function () {
+ var t = new User({
+ name: 'test validation'
+ });
+
+ t.validate(function (err) {
+ err.message.should.eql('Validation failed');
+ err.errors.required.type.should.equal('required');
+ t.required = 20;
+ t.save(function (err) {
+ err.name.should.eql('CastError');
+ err.type.should.eql('buffer');
+ err.value.should.equal(20);
+ err.message.should.eql('Cast to buffer failed for value "20"');
+ t.required = new Buffer("hello");
+
+ t.sub.push({ name: 'Friday Friday' });
+ t.save(function (err) {
+ err.message.should.eql('Validation failed');
+ err.errors.buf.type.should.equal('required');
+ t.sub[0].buf = new Buffer("well well");
+ t.save(function (err) {
+ err.message.should.eql('Validation failed');
+ err.errors.buf.type.should.equal('valid failed');
+
+ t.sub[0].buf = new Buffer("well well well");
+ t.validate(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ });
+ });
+ })
+ },
+
+ 'buffer storage': function(){
+ var db = start()
+ , User = db.model('UserBuffer', UserBuffer, 'usersbuffer_' + random());
+
+ User.on('index', function () {
+ var sampleBuffer = new Buffer([123, 223, 23, 42, 11]);
+
+ var tj = new User({
+ name: 'tj'
+ , serial: sampleBuffer
+ , required: new Buffer(sampleBuffer)
+ });
+
+ tj.save(function (err) {
+ should.equal(null, err);
+ User.find({}, function (err, users) {
+ db.close();
+ should.equal(null, err);
+ users.should.have.length(1);
+ var user = users[0];
+ var base64 = sampleBuffer.toString('base64');
+ should.equal(base64,
+ user.serial.toString('base64'), 'buffer mismatch');
+ should.equal(base64,
+ user.required.toString('base64'), 'buffer mismatch');
+ });
+ });
+ });
+ },
+
+ 'test write markModified': function(){
+ var db = start()
+ , User = db.model('UserBuffer', UserBuffer, 'usersbuffer_' + random());
+
+ User.on('index', function () {
+ var sampleBuffer = new Buffer([123, 223, 23, 42, 11]);
+
+ var tj = new User({
+ name: 'tj'
+ , serial: sampleBuffer
+ , required: sampleBuffer
+ });
+
+ tj.save(function (err) {
+ should.equal(null, err);
+
+ tj.serial.write('aa', 1, 'ascii');
+ tj.isModified('serial').should.be.true;
+
+ tj.save(function (err) {
+ should.equal(null, err);
+
+ User.findById(tj._id, function (err, user) {
+ db.close();
+ should.equal(null, err);
+
+ var expectedBuffer = new Buffer([123, 97, 97, 42, 11]);
+
+ should.equal(expectedBuffer.toString('base64'),
+ user.serial.toString('base64'), 'buffer mismatch');
+
+ tj.isModified('required').should.be.false;
+ tj.serial.copy(tj.required, 1);
+ tj.isModified('required').should.be.true;
+ should.equal('e3thYSo=', tj.required.toString('base64'));
+
+ // buffer method tests
+ var fns = {
+ 'writeUInt8': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt8(0x3, 0, 'big');
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt16': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt16(0xbeef, 0, 'little');
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt16LE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt16LE(0xbeef, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt16BE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt16BE(0xbeef, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt32': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt32(0xfeedface, 0, 'little');
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt32LE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt32LE(0xfeedface, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt32BE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt32BE(0xfeedface, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeInt8': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt8(-5, 0, 'big');
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeInt16': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt16(0x0023, 2, 'little');
+ tj.isModified('required').should.be.true;
+ tj.required[2].should.eql(0x23);
+ tj.required[3].should.eql(0x00);
+ }
+ , 'writeInt16LE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt16LE(0x0023, 2);
+ tj.isModified('required').should.be.true;
+ tj.required[2].should.eql(0x23);
+ tj.required[3].should.eql(0x00);
+ }
+ , 'writeInt16BE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt16BE(0x0023, 2);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeInt32': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt32(0x23, 0, 'big');
+ tj.isModified('required').should.be.true;
+ tj.required[0].should.eql(0x00);
+ tj.required[1].should.eql(0x00);
+ tj.required[2].should.eql(0x00);
+ tj.required[3].should.eql(0x23);
+ tj.required = new Buffer(8);
+ }
+ , 'writeInt32LE': function () {
+ tj.required = new Buffer(8);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt32LE(0x23, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeInt32BE': function () {
+ tj.required = new Buffer(8);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt32BE(0x23, 0);
+ tj.isModified('required').should.be.true;
+ tj.required[0].should.eql(0x00);
+ tj.required[1].should.eql(0x00);
+ tj.required[2].should.eql(0x00);
+ tj.required[3].should.eql(0x23);
+ }
+ , 'writeFloat': function () {
+ tj.required = new Buffer(16);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeFloat(2.225073858507201e-308, 0, 'big');
+ tj.isModified('required').should.be.true;
+ tj.required[0].should.eql(0x00);
+ tj.required[1].should.eql(0x0f);
+ tj.required[2].should.eql(0xff);
+ tj.required[3].should.eql(0xff);
+ tj.required[4].should.eql(0xff);
+ tj.required[5].should.eql(0xff);
+ tj.required[6].should.eql(0xff);
+ tj.required[7].should.eql(0xff);
+ }
+ , 'writeFloatLE': function () {
+ tj.required = new Buffer(16);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeFloatLE(2.225073858507201e-308, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeFloatBE': function () {
+ tj.required = new Buffer(16);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeFloatBE(2.225073858507201e-308, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeDoubleLE': function () {
+ tj.required = new Buffer(8);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeDoubleLE(0xdeadbeefcafebabe, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeDoubleBE': function () {
+ tj.required = new Buffer(8);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeDoubleBE(0xdeadbeefcafebabe, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'fill': function () {
+ tj.required = new Buffer(8);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.fill(0);
+ tj.isModified('required').should.be.true;
+ for (var i = 0; i < tj.required.length; i++) {
+ tj.required[i].should.eql(0);
+ }
+ }
+ , 'set': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.set(0, 1);
+ tj.isModified('required').should.be.true;
+ }
+ };
+
+ var keys = Object.keys(fns)
+ , i = keys.length
+ , key
+
+ while (i--) {
+ key = keys[i];
+ if (Buffer.prototype[key]) {
+ fns[key]();
+ }
+ }
+ });
+ });
+ });
+ });
+
+ function reset (model) {
+ // internal
+ model._activePaths.clear('modify');
+ model.schema.requiredPaths.forEach(function (path) {
+ model._activePaths.require(path);
+ });
+ }
+ }
+};
diff --git a/node_modules/mongoose/test/types.document.test.js b/node_modules/mongoose/test/types.document.test.js
new file mode 100644
index 0000000..a422403
--- /dev/null
+++ b/node_modules/mongoose/test/types.document.test.js
@@ -0,0 +1,197 @@
+
+/**
+ * Module dependencies.
+ */
+
+var should = require('should')
+ , start = require('./common')
+ , mongoose = start.mongoose
+ , EmbeddedDocument = require('../lib/types/embedded')
+ , DocumentArray = require('../lib/types/documentarray')
+ , Schema = mongoose.Schema
+ , SchemaType = mongoose.SchemaType
+ , ValidatorError = SchemaType.ValidatorError
+ , ValidationError = mongoose.Document.ValidationError
+
+/**
+ * Setup.
+ */
+
+function Subdocument () {
+ EmbeddedDocument.call(this, {}, new DocumentArray);
+};
+
+/**
+ * Inherits from EmbeddedDocument.
+ */
+
+Subdocument.prototype.__proto__ = EmbeddedDocument.prototype;
+
+/**
+ * Set schema.
+ */
+
+Subdocument.prototype.schema = new Schema({
+ test: { type: String, required: true }
+ , work: { type: String, validate: /^good/ }
+});
+
+/**
+ * Schema.
+ */
+
+var RatingSchema = new Schema({
+ stars: Number
+});
+
+var MovieSchema = new Schema({
+ title: String
+ , ratings: [RatingSchema]
+});
+
+mongoose.model('Movie', MovieSchema);
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that save fires errors': function(){
+ var a = new Subdocument();
+ a.set('test', '');
+ a.set('work', 'nope');
+
+ a.save(function(err){
+ err.should.be.an.instanceof(ValidationError);
+ err.toString().should.eql('ValidationError: Validator "required" failed for path test, Validator failed for path work');
+ });
+ },
+
+ 'test that save fires with null if no errors': function(){
+ var a = new Subdocument();
+ a.set('test', 'cool');
+ a.set('work', 'goods');
+
+ a.save(function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'objects can be passed to #set': function () {
+ var a = new Subdocument();
+ a.set({ test: 'paradiddle', work: 'good flam'});
+ a.test.should.eql('paradiddle');
+ a.work.should.eql('good flam');
+ },
+
+ 'Subdocuments can be passed to #set': function () {
+ var a = new Subdocument();
+ a.set({ test: 'paradiddle', work: 'good flam'});
+ a.test.should.eql('paradiddle');
+ a.work.should.eql('good flam');
+ var b = new Subdocument();
+ b.set(a);
+ b.test.should.eql('paradiddle');
+ b.work.should.eql('good flam');
+ },
+
+ 'cached _ids': function () {
+ var db = start();
+ var Movie = db.model('Movie');
+ db.close();
+ var m = new Movie;
+
+ should.equal(m.id, m.__id);
+ var old = m.id;
+ m._id = new mongoose.Types.ObjectId;
+ should.equal(m.id, m.__id);
+ should.strictEqual(true, old !== m.__id);
+
+ var m2= new Movie;
+ delete m2._doc._id;
+ m2.init({ _id: new mongoose.Types.ObjectId });
+ should.equal(m2.id, m2.__id);
+ should.strictEqual(true, m.__id !== m2.__id);
+ should.strictEqual(true, m.id !== m2.id);
+ should.strictEqual(true, m.__id !== m2.__id);
+ }
+
+ /*
+ 'Subdocument#remove': function (beforeExit) {
+ var db = start();
+ var Movie = db.model('Movie');
+
+ var super8 = new Movie({ title: 'Super 8' });
+
+ var id1 = '4e3d5fc7da5d7eb635063c96';
+ var id2 = '4e3d5fc7da5d7eb635063c97';
+ var id3 = '4e3d5fc7da5d7eb635063c98';
+ var id4 = '4e3d5fc7da5d7eb635063c99';
+
+ super8.ratings.push({ stars: 9, _id: id1 });
+ super8.ratings.push({ stars: 8, _id: id2 });
+ super8.ratings.push({ stars: 7, _id: id3 });
+ super8.ratings.push({ stars: 6, _id: id4 });
+ console.error('pre save', super8);
+
+ super8.save(function (err) {
+ should.strictEqual(err, null);
+
+ super8.title.should.eql('Super 8');
+ super8.ratings.id(id1).stars.valueOf().should.eql(9);
+ super8.ratings.id(id2).stars.valueOf().should.eql(8);
+ super8.ratings.id(id3).stars.valueOf().should.eql(7);
+ super8.ratings.id(id4).stars.valueOf().should.eql(6);
+
+ super8.ratings.id(id1).stars = 5;
+ super8.ratings.id(id2).remove();
+ super8.ratings.id(id3).stars = 4;
+ super8.ratings.id(id4).stars = 3;
+
+ super8.save(function (err) {
+ should.strictEqual(err, null);
+
+ Movie.findById(super8._id, function (err, movie) {
+ should.strictEqual(err, null);
+
+ movie.title.should.eql('Super 8');
+ movie.ratings.length.should.equal(3);
+ movie.ratings.id(id1).stars.valueOf().should.eql(5);
+ movie.ratings.id(id3).stars.valueOf().should.eql(4);
+ movie.ratings.id(id4).stars.valueOf().should.eql(3);
+
+ console.error('after save + findbyId', movie);
+
+ movie.ratings.id(id1).stars = 2;
+ movie.ratings.id(id3).remove();
+ movie.ratings.id(id4).stars = 1;
+
+ console.error('after modified', movie);
+
+ movie.save(function (err) {
+ should.strictEqual(err, null);
+
+ Movie.findById(super8._id, function (err, movie) {
+ db.close();
+ should.strictEqual(err, null);
+ movie.ratings.length.should.equal(2);
+ movie.ratings.id(id1).stars.valueOf().should.eql(2);
+ movie.ratings.id(id4).stars.valueOf().should.eql(1);
+ console.error('after resave + findById', movie);
+ });
+ });
+ });
+ });
+ });
+
+ beforeExit(function () {
+ var db = start();
+ Movie.remove({}, function (err) {
+ db.close();
+ });
+ });
+ }
+ */
+
+};
diff --git a/node_modules/mongoose/test/types.documentarray.test.js b/node_modules/mongoose/test/types.documentarray.test.js
new file mode 100644
index 0000000..ff8b381
--- /dev/null
+++ b/node_modules/mongoose/test/types.documentarray.test.js
@@ -0,0 +1,132 @@
+
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('./common').mongoose
+ , MongooseArray = mongoose.Types.Array
+ , MongooseDocumentArray = mongoose.Types.DocumentArray
+ , EmbeddedDocument = require('../lib/types/embedded')
+ , DocumentArray = require('../lib/types/documentarray')
+ , Schema = mongoose.Schema
+
+/**
+ * Setup.
+ */
+
+function TestDoc (schema) {
+ var Subdocument = function () {
+ EmbeddedDocument.call(this, {}, new DocumentArray);
+ };
+
+ /**
+ * Inherits from EmbeddedDocument.
+ */
+
+ Subdocument.prototype.__proto__ = EmbeddedDocument.prototype;
+
+ /**
+ * Set schema.
+ */
+
+ var SubSchema = new Schema({
+ title: { type: String }
+ });
+
+ Subdocument.prototype.schema = schema || SubSchema;
+
+ return Subdocument;
+}
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that a mongoose array behaves and quacks like an array': function(){
+ var a = new MongooseDocumentArray();
+
+ a.should.be.an.instanceof(Array);
+ a.should.be.an.instanceof(MongooseArray);
+ a.should.be.an.instanceof(MongooseDocumentArray);
+ Array.isArray(a).should.be.true;
+ a._atomics.constructor.name.should.equal('Object');
+ 'object'.should.eql(typeof a);
+
+ var b = new MongooseArray([1,2,3,4]);
+ 'object'.should.eql(typeof b);
+ Object.keys(b.toObject()).length.should.equal(4);
+ },
+
+ '#id': function () {
+ var Subdocument = TestDoc();
+
+ var sub1 = new Subdocument();
+ sub1.title = 'Hello again to all my friends';
+ var id = sub1.id;
+
+ var a = new MongooseDocumentArray([sub1]);
+ a.id(id).title.should.equal('Hello again to all my friends');
+ a.id(sub1._id).title.should.equal('Hello again to all my friends');
+
+ // test with custom string _id
+ var Custom = new Schema({
+ title: { type: String }
+ , _id: { type: String, required: true }
+ });
+
+ var Subdocument = TestDoc(Custom);
+
+ var sub2 = new Subdocument();
+ sub2.title = 'together we can play some rock-n-roll';
+ sub2._id = 'a25';
+ var id2 = sub2.id;
+
+ var a = new MongooseDocumentArray([sub2]);
+ a.id(id2).title.should.equal('together we can play some rock-n-roll');
+ a.id(sub2._id).title.should.equal('together we can play some rock-n-roll');
+
+ // test with custom number _id
+ var CustNumber = new Schema({
+ title: { type: String }
+ , _id: { type: Number, required: true }
+ });
+
+ var Subdocument = TestDoc(CustNumber);
+
+ var sub3 = new Subdocument();
+ sub3.title = 'rock-n-roll';
+ sub3._id = 1995;
+ var id3 = sub3.id;
+
+ var a = new MongooseDocumentArray([sub3]);
+ a.id(id3).title.should.equal('rock-n-roll');
+ a.id(sub3._id).title.should.equal('rock-n-roll');
+ },
+
+ 'inspect works with bad data': function () {
+ var threw = false;
+ var a = new MongooseDocumentArray([null]);
+ try {
+ a.inspect();
+ } catch (err) {
+ threw = true;
+ console.error(err.stack);
+ }
+ threw.should.be.false;
+ },
+
+ 'toObject works with bad data': function () {
+ var threw = false;
+ var a = new MongooseDocumentArray([null]);
+ try {
+ a.toObject();
+ } catch (err) {
+ threw = true;
+ console.error(err.stack);
+ }
+ threw.should.be.false;
+ }
+
+};
diff --git a/node_modules/mongoose/test/types.number.test.js b/node_modules/mongoose/test/types.number.test.js
new file mode 100644
index 0000000..68ebe5e
--- /dev/null
+++ b/node_modules/mongoose/test/types.number.test.js
@@ -0,0 +1,37 @@
+
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('./common').mongoose
+ , MongooseNumber = mongoose.Types.Number
+ , SchemaNumber = mongoose.Schema.Types.Number
+ , should = require('should')
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that a mongoose number behaves and quacks like a number': function(){
+ var a = new MongooseNumber(5);
+
+ a.should.be.an.instanceof(Number);
+ a.should.be.an.instanceof(MongooseNumber);
+ a.toString().should.eql('5');
+
+ (a._atomics.constructor).should.eql(Object);
+ },
+
+ 'an empty string casts to null': function () {
+ var n = new SchemaNumber();
+ should.strictEqual(n.cast(''), null);
+ },
+
+ 'a null number should castForQuery to null': function () {
+ var n = new SchemaNumber();
+ should.strictEqual(n.castForQuery(null), null);
+ }
+
+};
diff --git a/node_modules/mongoose/test/utils.test.js b/node_modules/mongoose/test/utils.test.js
new file mode 100644
index 0000000..b275578
--- /dev/null
+++ b/node_modules/mongoose/test/utils.test.js
@@ -0,0 +1,196 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , mongoose = start.mongoose
+ , Schema = mongoose.Schema
+ , utils = require('../lib/utils')
+ , StateMachine = require('../lib/statemachine')
+ , ObjectId = require('../lib/types/objectid')
+ , MongooseBuffer = require('../lib/types/buffer')
+
+/**
+ * Setup.
+ */
+
+var ActiveRoster = StateMachine.ctor('require', 'init', 'modify');
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'should detect a path as required if it has been required': function () {
+ var ar = new ActiveRoster();
+ ar.require('hello');
+ ar.stateOf('hello').should.equal('require');
+ },
+
+ 'should detect a path as inited if it has been inited': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.stateOf('hello').should.equal('init');
+ },
+
+ 'should detect a path as modified': function () {
+ var ar = new ActiveRoster();
+ ar.modify('hello');
+ ar.stateOf('hello').should.equal('modify');
+ },
+
+ 'should remove a path from an old state upon a state change': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.modify('hello');
+ ar.states.init.should.not.have.property('hello');
+ ar.states.modify.should.have.property('hello');
+ },
+
+ 'forEach should be able to iterate through the paths belonging to one state': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.init('goodbye');
+ ar.modify('world');
+ ar.require('foo');
+ ar.forEach('init', function (path) {
+ ['hello', 'goodbye'].should.contain(path);
+ });
+ },
+
+ 'forEach should be able to iterate through the paths in the union of two or more states': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.init('goodbye');
+ ar.modify('world');
+ ar.require('foo');
+ ar.forEach('modify', 'require', function (path) {
+ ['world', 'foo'].should.contain(path);
+ });
+ },
+
+ 'forEach should iterate through all paths that have any state if given no state arguments': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.init('goodbye');
+ ar.modify('world');
+ ar.require('foo');
+ ar.forEach(function (path) {
+ ['hello', 'goodbye', 'world', 'foo'].should.contain(path);
+ });
+ },
+
+ 'should be able to detect if at least one path exists in a set of states': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.modify('world');
+ ar.some('init').should.be.true;
+ ar.some('modify').should.be.true;
+ ar.some('require').should.be.false;
+ ar.some('init', 'modify').should.be.true;
+ ar.some('init', 'require').should.be.true;
+ ar.some('modify', 'require').should.be.true;
+ },
+
+ 'should be able to `map` over the set of paths in a given state': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.modify('world');
+ ar.require('iAmTheWalrus');
+ var suffixedPaths = ar.map('init', 'modify', function (path) {
+ return path + '-suffix';
+ });
+ suffixedPaths.should.eql(['hello-suffix', 'world-suffix']);
+ },
+
+ "should `map` over all states' paths if no states are specified in a `map` invocation": function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.modify('world');
+ ar.require('iAmTheWalrus');
+ var suffixedPaths = ar.map(function (path) {
+ return path + '-suffix';
+ });
+ suffixedPaths.should.eql(['iAmTheWalrus-suffix', 'hello-suffix', 'world-suffix']);
+ },
+
+ 'test utils.options': function () {
+ var o = { a: 1, b: 2, c: 3, 0: 'zero1' };
+ var defaults = { b: 10, d: 20, 0: 'zero2' };
+ var result = utils.options(defaults, o);
+ result.a.should.equal(1);
+ result.b.should.equal(2);
+ result.c.should.equal(3);
+ result.d.should.equal(20);
+ o.d.should.equal(result.d);
+ result['0'].should.equal('zero1');
+
+ var result2 = utils.options(defaults);
+ result2.b.should.equal(10);
+ result2.d.should.equal(20);
+ result2['0'].should.equal('zero2');
+
+ // same properties/vals
+ defaults.should.eql(result2);
+
+ // same object
+ defaults.should.not.equal(result2);
+ },
+
+ 'test deepEquals on ObjectIds': function () {
+ var s = (new ObjectId).toString();
+
+ var a = new ObjectId(s)
+ , b = new ObjectId(s);
+
+ utils.deepEqual(a, b).should.be.true;
+ utils.deepEqual(a, a).should.be.true;
+ utils.deepEqual(a, new ObjectId).should.be.false;
+ },
+
+ 'deepEquals on MongooseDocumentArray works': function () {
+ var db = start()
+ , A = new Schema({ a: String })
+ , M = db.model('deepEqualsOnMongooseDocArray', new Schema({
+ a1: [A]
+ , a2: [A]
+ }));
+
+ db.close();
+
+ var m1 = new M({
+ a1: [{a: 'Hi'}, {a: 'Bye'}]
+ });
+
+ m1.a2 = m1.a1;
+ utils.deepEqual(m1.a1, m1.a2).should.be.true;
+
+ var m2 = new M;
+ m2.init(m1.toObject());
+
+ utils.deepEqual(m1.a1, m2.a1).should.be.true;
+
+ m2.set(m1.toObject());
+ utils.deepEqual(m1.a1, m2.a1).should.be.true;
+ },
+
+ // gh-688
+ 'deepEquals with MongooseBuffer': function () {
+ var str = "this is the day";
+ var a = new MongooseBuffer(str);
+ var b = new MongooseBuffer(str);
+ var c = new Buffer(str);
+ var d = new Buffer("this is the way");
+ var e = new Buffer("other length");
+
+ utils.deepEqual(a, b).should.be.true;
+ utils.deepEqual(a, c).should.be.true;
+ utils.deepEqual(a, d).should.be.false;
+ utils.deepEqual(a, e).should.be.false;
+ utils.deepEqual(a, []).should.be.false;
+ utils.deepEqual([], a).should.be.false;
+ }
+
+};
diff --git a/node_modules/mongoose/test/zzlast.test.js b/node_modules/mongoose/test/zzlast.test.js
new file mode 100644
index 0000000..706b7f1
--- /dev/null
+++ b/node_modules/mongoose/test/zzlast.test.js
@@ -0,0 +1,11 @@
+
+var should = require('should');
+var gleak = require('gleak')();
+gleak.whitelist.push('currentObjectStored', 'reg_exp'); // node-mongodb-native
+
+module.exports.globals = function (beforeExit) {
+ beforeExit(function () {
+ var leaks = gleak.detect();
+ should.strictEqual(leaks.length, 0, 'detected global var leaks: ' + leaks.join(', '));
+ })
+}