pax_global_header00006660000000000000000000000064123232767500014521gustar00rootroot0000000000000052 comment=4c1d928067f6b6bcf03a30e16d94f4efc157cdfb databank-0.19.1/000077500000000000000000000000001232327675000133565ustar00rootroot00000000000000databank-0.19.1/.gitignore000066400000000000000000000001371232327675000153470ustar00rootroot00000000000000lib-cov *.seed *.log *.csv *.dat *.out *.pid *.gz pids logs results node_modules npm-debug.log databank-0.19.1/.travis.yml000066400000000000000000000000711232327675000154650ustar00rootroot00000000000000language: node_js node_js: - "0.10" - "0.8" - "0.6"databank-0.19.1/CACHING.md000066400000000000000000000017261232327675000147420ustar00rootroot00000000000000caching driver -------------- The caching driver doesn't directly provide storage. Instead, it provides a storage pattern -- using fast and easy storage for quick retrieval, and slower storage for long-term persistence. Hopefully using this driver will keep you from having to write caching code in your own app. Usage ===== To create a caching databank, use the `Databank.get()` method: var Databank = require('databank').Databank; var db = Databank.get('caching', {cache: {driver: 'memory', params: {}}, source: {driver: 'disk', params: {dir: '/var/lib/mine/'}}}); The driver takes the following parameters: * `schema`: the database schema, as described in the Databank README. * `source`: the source databank info. Must be an object with `driver` and `params` properties. * `cache`: the cache databank info. Must be an object with `driver` and `params` properties. If not set, a new `memory` databank will be used. databank-0.19.1/LICENSE000066400000000000000000000261361232327675000143730ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. databank-0.19.1/MEMORY.md000066400000000000000000000020351232327675000147100ustar00rootroot00000000000000memory driver ------------- The memory driver provides in-memory storage of data. It is *not* persistent; data will be lost after the process stops. It works well in conjunction with the caching driver (q.v.), and for unit tests. Usage ===== To create a memory databank, use the `Databank.get()` method: var Databank = require('databank').Databank; var db = Databank.get('memory', {}); The driver takes the following parameters: * `schema`: the database schema, as described in the Databank README. * `data`: initial data for the databank. It must be an object, with properties mapped to types, each of which maps to a map of key-value pairs. For example: var db = Databank.get('memory', {data: {state: {'KY': {name: 'Kentucky'}, 'KS': {name: 'Kansas'}}}, {city: {'LAX': {name: 'Los Angeles'}, 'NYC': {name: 'New York City'}}}}); databank-0.19.1/PARTITIONING.md000066400000000000000000000037351232327675000156170ustar00rootroot00000000000000partitioning driver ------------------- The partitioning driver lets you put different types into different databanks. It lets you defer decisions about database partitioning until configuration time, so you don't have to worry about that kind of thing within your application code. Usage ===== To create a partitioning databank, use the `Databank.get()` method: var Databank = require('databank').Databank, params = { 'session': {'driver': 'memory', 'params': {}}, 'token': {'driver': 'memcached', 'params': {'serverLocations': ['192.168.3.1:11211', '192.168.3.1:11211']}}, '*': {'driver': 'mongodb', 'params': {'host': 'myhost.example', 'dbname': 'mydb'}}, }; var db = Databank.get('partitioning', params); // Just use databank calls as usual db.create('session', 123, {nickname: 'evanp', size: 'xl'}, function(err, newSession) { console.dir(newSession); }); // Comes from the default databank db.read('address', '1444 Elm Street', function(err, readAddress) { console.dir(readAddress); }); Unlike most databank drivers, the partitioning driver doesn't take a fixed set of params. Instead, the parameters are a map of type names to databank connection definitions with `driver` and `params` values. In the above example: * All `session` data will be stored in a memory databank * All `token` data will be stored in memcached (actually, it could also be Couchbase, but you get the picture) * All other types will be stored in a mongodb databank. Note that the special param '*' defines a default driver. You should usually have one, unless you're sure you mapped all your types. Bugs ==== * You can only partition vertically by type for now. Horizontal partitioning (by key) will probably happen in the future. * You can't have a type named '*'. You probably don't need this unless you're a proctologist or an astronomer. * You can't make two types share the same databank instance. databank-0.19.1/README.md000066400000000000000000000432141232327675000146410ustar00rootroot00000000000000# Databank This package is an abstraction tool for document stores or key-value stores in Node.js. My goal is to hedge my bets by using a simple CRUD + search interface for interacting with a datastore. If at some point I really need the special snowflake features of Redis or MongoDB or Cassandra or Riak or whatever, I should be able to bust out of this simple abstraction and use their native interface without rewriting a lot of code. I also want the data structures stored to look roughly like what someone experienced with the datastore would expect. I chose the name "databank" since it's not in widespread use and won't cause name conflicts, and because it sounds like something a 1960s robot would say. As a note: I've used this library for a couple of big projects, and mostly it just works. # License Copyright 2011-2014 E14N https://e14n.com/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at > http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. # Drivers The point of the Databank interface is so applications can use one interface for developing persistence code, and then at deployment time you can decide what driver to use. There are three drivers included in this package: 'memory', 'partitioning', and 'caching'. The first is great for development but pretty bad for production. There are a few drivers not in this package. You can search for them on npm; they all start with 'databank-'. So, 'databank-leveldb', 'databank-mongodb', 'databank-memcached', 'databank-redis', 'databank-disk'. ## Installation I'm still not 100% on this, so comments welcome. I'd love better instructions. At deployment time, if you want to use a particular driver, it's going to need to be available to the 'databank' libraries so that `Databank.get()` can find it. This means you have two options: * Install the driver globally, like `npm install -g databank-redis`. This is probably OK as long as you don't have version conflicts between apps. * Install the driver in the `databank` dir, like so: npm install databank cd node_modules/databank/ npm install databank-redis If you're still stuck, there's a `Databank.register()` method that will let you associate a databank driver class with a driver name. That's probably only a last resort, though. ## Built-in drivers The built-in drivers are documented in MEMORY.md, CACHING.md, and PARTITIONING.md respectively. # Schemata This library assumes you have document "types" - like "person", "chair", "photo", "bankaccount", "trainreservation" -- that you can identify with a unique scalar key -- email address, URL, UUID, SSN, or whatever. Your "document" is anything that can be JSON-encoded and decoded. Scalar, array and object/tree values are all totally cool. Implementation classes that support schemata should support a "schema" element on the constructor params for `Databank.get()` (see below). A schema can have elements for each type, with the following elements: * pkey: the primary key element name. * indices: array of element names that should be indexed. You should really have an index on each element you search on frequently. ## Dotted notation In schemata you can use dotted-notation, a la MongoDB, to define fields that are part of parts of the object. For example, for an object like this: { email: "evan@e14n.com", name: { last: "Prodromou", first: "Evan" } } ...you may have a schema like this: { person: { pkey: "email", indices: ["name.last"] } } # Databank The class has a static method for for initializing an instance: * `get(driver, params)` Get an instance of `DriverDatabank` from the module `databank-driver` and initialize it with the provided params (passed as a single object). This is the place you should usually pass in a schema parameter. var bank = Databank.get('redis', {schema: {person: {pkey: "email"}}}); bank.connect({}, function(err) { if (err) { console.log("Couldn't connect to databank: " + err.message); } else { // ... } }); There's another static method to change how `get()` works: * `register(driver, cls)` Register a class to use when `get()` asks for `driver`. This is useful if for some reason your driver class isn't exported by the `databank-driver` package somewhere. (I mainly use this for unit-testing drivers.) The databank interface has these methods: * `connect(params, onCompletion)` Connect to the databank. `params` may be used by the underlying server. `onCompletion` takes one argument: a `DatabankError` object. Null if no error. * `disconnect(onCompletion)` Disconnect from the databank. `onCompletion` takes one argument, a DatabankError. * `create(type, id, value, onCompletion)` Create a databank entry of type `type` with id `id` and content `value`. How `type` and `id` are mapped to keys or whatever in the DB is unspecified. Don't mix and match. `onCompletion` takes two arguments: a `DatabankError` (or null) and the created object. That created object may have some extra stuff added on. Common error type here is `AlreadyExistsError`. store.create('activity', uuid, activity, function(err, value) { if (err instanceof AlreadyExistsError) { res.writeHead(409, {'Content-Type': 'application/json'}); res.end(JSON.stringify(err.message)); } else if (err) { res.writeHead(400, {'Content-Type': 'application/json'}); res.end(JSON.stringify(err.message)); } else { res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify(value)); } }); * `read(type, id, onCompletion)` Read an object of type `type` with id `id` from the databank. `onCompletion` will get two arguments: a `DatabankError` (or null) and the object if found. Common error type here is `NoSuchThingError` if the databank has no such object. bank.read('Book', '978-0141439600', function(err, user) { if (err instanceof NoSuchThingError) { res.writeHead(404, {'Content-Type': 'application/json'}); res.end(JSON.stringify(err.message)); } else if (err) { res.writeHead(500, {'Content-Type': 'application/json'}); res.end(JSON.stringify(err.message)); } else { res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify(user)); } }); * `update(type, id, value, onCompletion)` Update the (existing) object of type `type` with id `id` in the databank. `onCompletion` will get two arguments: a `DatabankError` (or null) and the object if found. Common error type here is `NoSuchThingError` if the databank has no such object. * `save(type, id, value, onCompletion)` Either create a new object, or update an existing object. For when you don't care which. * `del(type, id, onCompletion)` Delete the object of type `type` with id `id`. `onCompletion` takes one argument, a `DatabankError` (null on success). "delete" is a keyword, so I decided not to use that. * `search(type, criteria, onResult, onCompletion)` Finds objects of type `type` which match `criteria`, a map of property names to exact value matches. `onResult` is called one time for each result, with a single argument, the object that matches the criteria. Use a collector array if you want all the results in an array. Property names can be dotted to indicate deeper structures; for example, this object: {name: {last: "Prodromou", first: "Evan"}, age: 43} would match the criteria `{"name.last": "Prodromou"}`. `onCompletion` takes one argument, a `DatabankError`. A search with no results will get a `NoSuchThingError`. I think this is the method most likely to elicit a `NotImplementedError`, since most key-value stores don't handle this kind of thing. You're also on your own on sorting. function getModerators(callback) { var results = []; bank.search('user', {role: 'moderator'}, function(result) { results.push(result); }, function(err) { if (err) { callback(err, null); } else { results.sort(function(a, b) { return a.created - b.created; }); callback(null, results); } }); } * `scan(type, onResult, onCompletion)` Finds all objects of type `type`. `onResult` is called one time for each result, with a single argument, the object that matches the criteria. Use a collector array if you want all the results in an array. `onCompletion` takes one argument, a `DatabankError`. A search with no results will get a `NoSuchThingError`. I think this is the method most likely to elicit a `NotImplementedError`, since most key-value stores don't handle this kind of thing. This is probably most useful for off-line processing, like doing a backup or for initializing roll-up data. At scale, this may take days to complete. If you want to do something like searching a range, figure out a better way, like storing an array of matches at write time. * `readAll(type, ids, onCompletion)` Gets all the objects of type `type` with ids in the array `ids`. Results are an object mapping an id to the results. If an ID doesn't exist, the mapped value will be `null`. This is kind of like calling `read` over and over, but if the driver supports multiple reads in one call, it can be much more performant. `onCompletion` gets two arguments: an error, and the results map. ## Integers These are special shims for integer values. * `incr(type, id, onCompletion)` Increments the integer value of `type` and `id` by one. `onCompletion` takes two params: an error, and the resulting integer value. If integer value doesn't yet exists, goes to 1. Defaults to a `read` and an `update` or `create`, but drivers can override to do an atomic increment. * `decr(type, id, onCompletion)` Increments the integer value of `type` and `id` by one. `onCompletion` takes two params: an error, and the resulting integer value. If integer value doesn't yet exists, goes to -1. Defaults to a `read` and an `update` or `create`, but drivers can override to do an atomic decrement. ## Arrays These are special shims for array values. * `append(type, id, toAppend, onCompletion)` Appends the value `toAppend` to the array at `type` and `id`. `onCompletion` takes one param: an error. If array doesn't yet exists, it becomes a single-element array. Defaults to a `read` and an `update` or `create`, but drivers can override to do an atomic append. * `prepend(type, id, toPrepend, onCompletion)` Prepends the value `toPrepend` to the array at `type` and `id`. `onCompletion` takes one param: an error. If array doesn't yet exists, it becomes a single-element array. Defaults to a `read` and an `update` or `create`, but drivers can override to do an atomic prepend. * `item(type, id, index, onCompletion)` Gets the value at `index` in the array at `type` and `id`. `onCompletion` takes two params: an error, and the resulting item value. Defaults to read the whole array and pluck out the value, but some drivers might support atomic query of just one item. * `slice(type, id, start, length, onCompletion)` Like `Array.slice()`, gets the sub-array starting at index `start` of length `length` of the array at `type` and `id`. `onCompletion` takes two params: `err` for error, and `results` for the resulting slice. Defaults to read the whole array and pluck out the slice, but some drivers might support atomic query of a slice. * `indexOf(type, id, item, onCompletion)` Like `Array.indexOf()`, gets the first index of `item` in the array at `type` and `id`. `onCompletion` takes two params: `err` for error, and `index` for the resulting index. Will give an index of -1 (like Javascript) on a miss. * `remove(type, id, item, onCompletion)` Like `Array.remove()`, removes the first instance of `item` in the array at `type` and `id`. `onCompletion` takes one params: `err` for error. # DatabankError This is a subclass of `Error` for stuff that went wrong with a `Databank`. Subclasses include: * `NotImplementedError` That doesn't work (yet). * `NoSuchThingError` The type/id pair you were trying to read/update/delete doesn't exist. * `AlreadyExistsError` The type/id pair you were trying to create *does* exist. * `NotConnectedError` You forgot to call `connect` first. * `AlreadyConnectedError` You already called `connect`. * `NoSuchItemError` There's no item in that array with that value. * `WrongTypeError` You tried to use one of the array operators on a non-array value. # DatabankObject This is a utility class for objects you want to store in a Databank. To create the class, do this: var MyClass = DatabankObject.subClass('mytype'); This will make an object class that stores data in the 'mytype' type. You can add more stuff to the class, of course. The class's `type` is stored in `MyClass.type`. The constructor takes an object as a parameter; it will copy all its properties from this object. Good for "classifying" JSON. So: var json = getSomeJSONfromSomewhere(); var myInst = new MyClass(json); Each class has the following class methods: * `bank()` Gets the class's databank. Used internally for making queries. By default, gets the DatabankObject.bank property. If you want to change how this works, replace this function with... something else. * `pkey()` Gets the class's primary key. By default, looks for a class attribute "schema" and tries to get the "pkey" element of that. Otherwise, it checks the class's schema, looks for an element that matches the type name, and tries to get pkey element of that. If that fails, it looks at the class's databank's "schema", and tries to get that. Otherwise, it just returns "id". Override if you have a better plan. * `get(id, callback)` Get the object with primary key `id` and returns it to the `callback`. * `search(criteria, callback)` Does a search for objects matching the criteria, collects them, and returns an array to `callback`. * `scan(handler, callback)` Finds all objects of this type and calls `handler` on each one. At the end, fires `callback` with a single `err` parameter. * `create(properties, callback)` Creates a new instance of class with `properties` and returns it to callback. * `readAll(ids, callback)` Reads all objects from the databank with the given array of primary-key ids, and returns a map of {id: object}. * `readArray(ids, callback)` Reads all objects from the databank with the given array of primary-key ids, and returns an array of objects in the same order. Each instance has the following methods: * `update(properties, callback)` For an existing object, update to the provided properties, and return the resulting object to `callback`. Note that you can use only a few properties; note that you can't use this method to _remove_ properties. * `del(callback)` Delete the object. `callback` takes a single error arg. * `save(callback)` Save the current state of the object, and return it to `callback`. Will create new objects or update existing ones. ## Hooks When I started using this library, I found myself overloading the create(), update(), and save() methods to do extra things, like add an auto-generated ID or timestamp, or to expand attributes stored by reference. It was a little tricky, since I had to save off the default auto-created function, then define a new function that called that saved one. To make this easier, I added a hooks mechanism. Now, every DatabankObject subclass has the option of hooking certain functionality without having to replicate the core functionality. Default values are all no-ops. Class methods: * `beforeCreate(props, callback)` Called before `create()`. A chance to add default values or validate. `callback` takes two args: an err, or the (possibly modified) props. Instance methods: * `afterCreate(callback)` Called after `create()`. Good chance to save references. `callback` takes one arg: an err. * `beforeGet(id, callback)` Called before `get()`. I don't see a lot of reason to mess with this, but it's here if you need it. `callback` takes two args: an err, or the (possibly modified) id. * `afterGet(callback)` Called after `get()`. Good chance to expand references. `callback` takes one arg: an err. This is also called once for each instance returned in `readAll()`. * `beforeUpdate(props, callback)` Called before `update()`. Validate, preserve immutables, or add auto-generated properties. `callback` takes two args: an err and the (possibly modified) props. * `afterUpdate(callback)` Called after `update()`. `callback` takes one arg: an err. * `beforeDel(callback)` Called before `del()`. Maybe prevent deleting something important? Referential integrity? `callback` takes one arg: an err. * `afterDel(callback)` Called after `del()`. Delete related stuff? `callback` takes one arg: an err. * `beforeSave(callback)` Called before `save()`. Validate, preserve, autogenerate. `callback` takes one args: an err. * `afterSave(callback)` Called after `save()`. `callback` takes one args: an err. TODO ---- See https://github.com/e14n/databank/issues databank-0.19.1/lib/000077500000000000000000000000001232327675000141245ustar00rootroot00000000000000databank-0.19.1/lib/databank.js000066400000000000000000000356141232327675000162400ustar00rootroot00000000000000// databank.js // // abstraction for storing JSON data in some kinda storage // // Copyright 2011,2012 E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // I'm too much of a wuss to commit to any JSON storage mechanism right // now, so I'm just going to declare the interface I need and try a few // different systems to implement it. // A thing that stores JSON. // Basically CRUD + Search. Recognizes types of data without // enforcing a schema. var Step = require('step'); function Databank(params) { } Databank.localDriver = function(driver) { return './drivers/' + driver.toLowerCase(); }; Databank.drivers = {}; Databank.register = function(name, cls) { Databank.drivers[name] = cls; return; }; Databank.deepProperty = function(object, property) { var i = property.indexOf('.'); if (!object) { return null; } else if (i == -1) { // no dots return object[property]; } else { return Databank.deepProperty(object[property.substr(0, i)], property.substr(i + 1)); } }; Databank.get = function(driver, params) { var core = ['memory', 'caching', 'partitioning'], canon = driver.toLowerCase(), cls; if (core.indexOf(canon) !== -1) { cls = require(Databank.localDriver(driver)); } else if (Databank.drivers.hasOwnProperty(driver)) { cls = Databank.drivers[driver]; } else { cls = require("databank-" + canon); } return new cls(params); }; Databank.prototype = { // Connect yourself on up. // params: object containing any params you need // onCompletion(err): function to call on completion connect: function(params, onCompletion) { if (onCompletion) { onCompletion(new NotImplementedError()); } }, // Disconnect yourself. // onCompletion(err): function to call on completion disconnect: function(onCompletion) { if (onCompletion) { onCompletion(new NotImplementedError()); } }, // Create a new thing // type: string, type of thing, usually 'user' or 'activity' // id: a unique ID, like a nickname or a UUID // value: JavaScript value; will be JSONified // onCompletion(err, value): function to call on completion create: function(type, id, value, onCompletion) { if (onCompletion) { onCompletion(new NotImplementedError(), null); } }, // Read an existing thing // type: the type of thing; 'user', 'activity' // id: a unique ID -- nickname or UUID or URI // onCompletion(err, value): function to call on completion read: function(type, id, onCompletion) { if (onCompletion) { onCompletion(new NotImplementedError(), null); } }, // Update an existing thing // type: the type of thing; 'user', 'activity' // id: a unique ID -- nickname or UUID or URI // value: the new value of the thing // onCompletion(err, value): function to call on completion update: function(type, id, value, onCompletion) { if (onCompletion) { onCompletion(new NotImplementedError(), null); } }, // Delete an existing thing // type: the type of thing; 'user', 'activity' // id: a unique ID -- nickname or UUID or URI // value: the new value of the thing // onCompletion(err): function to call on completion del: function(type, id, onCompletion) { if (onCompletion) { onCompletion(new NotImplementedError()); } }, // Search for things // type: type of thing // criteria: map of criteria, with exact matches, // like {'subject.id':'tag:example.org,2011:evan' } // onResult(value): called once per result found // onCompletion(err): called once at the end of results search: function(type, criteria, onResult, onCompletion) { if (onCompletion) { onCompletion(new NotImplementedError()); } }, // Get all things of a particular type // type: type of thing // onResult(value): called once per result found // onCompletion(err): called once at the end of results scan: function(type, onResult, onCompletion) { if (onCompletion) { onCompletion(new NotImplementedError()); } }, // Update an existing thing // type: the type of thing; 'user', 'activity' // id: a unique ID -- nickname or UUID or URI // value: the new value of the thing // onCompletion(err, value): function to call on completion save: function(type, id, value, onCompletion) { var bank = this; bank.update(type, id, value, function(err, result) { if (err instanceof NoSuchThingError) { bank.create(type, id, value, function(err, result) { onCompletion(err, result); }); } else { onCompletion(err, result); } }); }, // Read a bunch of things from the db // type: the type of thing; 'user', 'activity' // id: array of IDs // onCompletion(err, results): function to call on completion; // err: an error or null if none // results: map of id (maybe stringified) to value readAll: function(type, ids, onCompletion) { var bank = this, readNice = function(type, id, callback) { bank.read(type, id, function(err, value) { if (err) { if (err instanceof NoSuchThingError) { callback(null, null); } else { callback(err, null); } } else { callback(null, value); } }); }; Step( function() { var i, group = this.group(); for (i = 0; i < ids.length; i++) { readNice(type, ids[i], group()); } }, function(err, items) { var i, results; if (err) { onCompletion(err, null); } else { results = {}; for (i = 0; i < ids.length; i++) { results[ids[i]] = items[i]; } onCompletion(null, results); } } ); }, readAndModify: function(type, id, def, modify, onCompletion) { var bank = this; bank.read(type, id, function(err, value) { if (err) { // Set to def if not exist if (err instanceof NoSuchThingError) { bank.create(type, id, def, function(err, created) { if (err) { if (err instanceof AlreadyExistsError) { // Race condition; try again bank.readAndModify(type, id, def, modify, onCompletion); } else { onCompletion(err, null); } } else { onCompletion(null, created); } }); } else { onCompletion(err, null); } } else { bank.update(type, id, modify(value), onCompletion); } }); }, incr: function(type, id, onCompletion) { this.readAndModify(type, id, 1, function(value) { return value+1; }, onCompletion); }, decr: function(type, id, onCompletion) { this.readAndModify(type, id, -1, function(value) { return value-1; }, onCompletion); }, append: function(type, id, toAppend, onCompletion) { this.readAndModify(type, id, [toAppend], function(value) { value.push(toAppend); return value; }, function(err, result) { onCompletion(err); }); }, prepend: function(type, id, toPrepend, onCompletion) { this.readAndModify(type, id, [toPrepend], function(value) { value.unshift(toPrepend); return value; }, function(err, result) { onCompletion(err); }); }, item: function(type, id, index, onCompletion) { this.read(type, id, function(err, value) { if (err) { onCompletion(err, null); } else { if (Array.isArray(value)) { onCompletion(null, value[index]); } else { onCompletion(new WrongTypeError(type, id, "array"), null); } } }); }, slice: function(type, id, start, length, onCompletion) { this.read(type, id, function(err, value) { if (err) { onCompletion(err, null); } else { if (Array.isArray(value)) { onCompletion(null, value.slice(start, length)); } else { onCompletion(new WrongTypeError(type, id, "array"), null); } } }); }, indexOf: function(type, id, item, onCompletion) { this.read(type, id, function(err, value) { if (err) { onCompletion(err, null); } else { if (Array.isArray(value)) { onCompletion(null, value.indexOf(item)); } else { onCompletion(new WrongTypeError(type, id, "array"), null); } } }); }, remove: function(type, id, item, onCompletion) { var bank = this; bank.read(type, id, function(err, value) { var i; if (err) { onCompletion(err, null); } else { if (Array.isArray(value)) { i = value.indexOf(item); if (i === -1) { onCompletion(new NoSuchItemError(type, id, item)); } else { value.splice(i, 1); bank.update(type, id, value, function(err, value) { if (err) { onCompletion(err); } else { onCompletion(null); } }); } } else { onCompletion(new WrongTypeError(type, id, "array"), null); } } }); }, // utility for searches matchesCriteria: function(value, criteria) { var property; for (property in criteria) { if (Databank.deepProperty(value, property) != criteria[property]) { return false; } } return true; } }; // A custom error for Databank schtuff. var DatabankError = function(message) { Error.captureStackTrace(this, DatabankError); this.name = 'DatabankError'; this.message = message || "Databank error"; }; DatabankError.prototype = new Error(); DatabankError.prototype.constructor = DatabankError; var NoSuchThingError = function(type, id) { Error.captureStackTrace(this, NoSuchThingError); this.name = 'NoSuchThingError'; this.type = type; this.id = id; this.message = "No such '" + type + "' with id '" + id + "'"; }; NoSuchThingError.prototype = new DatabankError(); NoSuchThingError.prototype.constructor = NoSuchThingError; var AlreadyExistsError = function(type, id) { Error.captureStackTrace(this, AlreadyExistsError); this.name = 'AlreadyExistsError'; this.type = type; this.id = id; this.message = "Already have a(n) '" + type + "' with id '" + id + "'"; }; AlreadyExistsError.prototype = new DatabankError(); AlreadyExistsError.prototype.constructor = AlreadyExistsError; var NoSuchItemError = function(type, id, item) { Error.captureStackTrace(this, NoSuchItemError); this.name = 'NoSuchItemError'; this.type = type; this.id = id; this.item = item; this.message = "No such index '" + item + "' in '" + type + "' with key '" + id + "'"; }; NoSuchItemError.prototype = new DatabankError(); NoSuchItemError.prototype.constructor = NoSuchItemError; var WrongTypeError = function(type, id, expected) { Error.captureStackTrace(this, WrongTypeError); this.name = 'WrongTypeError'; this.type = type; this.id = id; this.expected = expected; this.message = "The '" + type + "' with key '" + id + "' should be a '" + expected + "'"; }; WrongTypeError.prototype = new DatabankError(); WrongTypeError.prototype.constructor = WrongTypeError; var NotImplementedError = function() { Error.captureStackTrace(this, NotImplementedError); this.name = 'NotImplementedError'; this.message = "Method not yet implemented."; }; NotImplementedError.prototype = new DatabankError(); NotImplementedError.prototype.constructor = NotImplementedError; var NotConnectedError = function() { Error.captureStackTrace(this, NotConnectedError); this.name = 'NotConnectedError'; this.message = "Not connected to a server."; }; NotConnectedError.prototype = new DatabankError(); NotConnectedError.prototype.constructor = NotConnectedError; var AlreadyConnectedError = function() { Error.captureStackTrace(this, AlreadyConnectedError); this.name = 'AlreadyConnectedError'; this.message = "Already connected to a server."; }; AlreadyConnectedError.prototype = new DatabankError(); AlreadyConnectedError.prototype.constructor = AlreadyConnectedError; exports.Databank = Databank; exports.DatabankError = DatabankError; exports.NotImplementedError = NotImplementedError; exports.NoSuchThingError = NoSuchThingError; exports.AlreadyExistsError = AlreadyExistsError; exports.AlreadyConnectedError = AlreadyConnectedError; exports.NotConnectedError = NotConnectedError; exports.NoSuchItemError = NoSuchItemError; exports.WrongTypeError = WrongTypeError; databank-0.19.1/lib/databankobject.js000066400000000000000000000231431232327675000174210ustar00rootroot00000000000000// databankobject.js // // abstraction for CRUD'ing an object as JSON to a Databank // // Copyright 2011, 2012 E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var Step = require('step'), Databank = require('./databank').Databank; var DatabankObject = function(properties) { DatabankObject.init(this, properties); }; DatabankObject.init = function(obj, properties) { DatabankObject.copy(obj, properties); }; DatabankObject.copy = function(obj, properties) { var property; for (property in properties) { obj[property] = properties[property]; } }; DatabankObject.driver = null; DatabankObject.params = {}; DatabankObject.bank = null; // Utilities for default hooks var passThru0 = function(callback) { callback(null); }; var passThru1 = function(arg, callback) { callback(null, arg); }; // Default hooks DatabankObject.beforeCreate = passThru1; DatabankObject.prototype.afterCreate = passThru0; DatabankObject.beforeGet = passThru1; DatabankObject.prototype.afterGet = passThru0; DatabankObject.prototype.beforeUpdate = passThru1; DatabankObject.prototype.afterUpdate = passThru0; DatabankObject.prototype.beforeDel = passThru0; DatabankObject.prototype.afterDel = passThru0; DatabankObject.prototype.beforeSave = passThru0; DatabankObject.prototype.afterSave = passThru0; DatabankObject.subClass = function(type, Parent) { var Cls; if (!Parent) { Parent = DatabankObject; } Cls = function(properties) { Cls.init(this, properties); }; Cls.init = function(inst, properties) { Parent.init(inst, properties); }; Cls.parent = Parent; Cls.beforeCreate = Parent.beforeCreate || DatabankObject.beforeCreate; Cls.beforeGet = Parent.beforeGet || DatabankObject.beforeGet; Cls.bank = function() { return DatabankObject.bank; }; Cls.type = type; Cls.get = function(id, callback) { Cls.beforeGet(id, function(err) { if (err) { callback(err, null); } else { Cls.bank().read(Cls.type, id, function(err, value) { var c; if (err) { callback(err, null); } else { c = new Cls(value); c.afterGet(function(err) { if (err) { callback(err, null); } else { callback(null, c); } }); } }); } }); }; Cls.search = function(criteria, callback) { var results = []; Cls.bank().search(Cls.type, criteria, function(value) { results.push(new Cls(value)); }, function(err) { if (err) { callback(err, null); } else { callback(null, results); } }); }; Cls.scan = function(each, callback) { Cls.bank().scan(Cls.type, function(value) { each(new Cls(value)); }, callback); }; Cls.pkey = function() { var bank; if (Cls.schema && Cls.schema.pkey) { return Cls.schema.pkey; } if (Cls.schema && Cls.schema[Cls.type] && Cls.schema[Cls.type].pkey) { return Cls.schema[Cls.type].pkey; } bank = Cls.bank(); if (bank && bank.schema && bank.schema[Cls.type] && bank.schema[Cls.type].pkey) { return bank.schema[Cls.type].pkey; } return "id"; }; Cls.create = function(orig, callback) { Cls.beforeCreate(orig, function(err, props) { if (err) { callback(err, null); } else { Cls.bank().create(Cls.type, props[Cls.pkey()], props, function(err, value) { var inst; if (err) { callback(err, null); } else { inst = new Cls(value); inst.afterCreate(function(err) { if (err) { callback(err, null); } else { callback(null, inst); } }); } }); } }); }; Cls.readAll = function(ids, callback) { var clsMap = {}; Step( function() { var i, group = this.group(); for (i = 0; i < ids.length; i++) { Cls.beforeGet(ids[i], group()); } }, function(err, results) { if (err) throw err; ids = results; Cls.bank().readAll(Cls.type, ids, this); }, function(err, map) { var id, group = this.group(); if (err) throw err; for (id in map) { if (map.hasOwnProperty(id)) { if (map[id]) { clsMap[id] = new Cls(map[id]); clsMap[id].afterGet(group()); } else { clsMap[id] = null; group()(null); } } } }, function(err) { if (err) { callback(err, null); } else { callback(null, clsMap); } } ); }; Cls.readArray = function(ids, callback) { Step( function() { Cls.readAll(ids, this); }, function(err, map) { var i, results; if (err) { callback(err); } else { results = new Array(ids.length); for (i = 0; i < ids.length; i++) { results[i] = map[ids[i]]; } callback(null, results); } } ); }; Cls.prototype = new Parent({ update: function(orig, callback) { var inst = this; inst.beforeUpdate(orig, function(err, props) { if (err) { callback(err, null); } else { DatabankObject.copy(inst, props); Cls.bank().update(Cls.type, inst[Cls.pkey()], inst, function(err, value) { if (err) { callback(err, null); } else { DatabankObject.copy(inst, value); // may be updated inst.afterUpdate(function(err) { if (err) { callback(err, null); } else { callback(null, inst); } }); } }); } }); }, del: function(callback) { var inst = this; inst.beforeDel(function(err) { if (err) { callback(err); } else { Cls.bank().del(Cls.type, inst[Cls.pkey()], function(err) { if (err) { callback(err); } else { inst.afterDel(callback); } }); } }); }, save: function(callback) { var inst = this; inst.beforeSave(function(err) { if (err) { callback(err); } else { Cls.bank().save(Cls.type, inst[Cls.pkey()], inst, function(err, value) { if (err) { callback(err, null); } else { DatabankObject.copy(inst, value); // may be updated inst.afterSave(function(err) { if (err) { callback(err, null); } else { callback(null, inst); } }); } }); } }); }, toString: function() { var obj = this, id = obj[Cls.pkey()] || "Undefined"; return "[" + Cls.type + " " + id + "]"; } }); return Cls; }; exports.DatabankObject = DatabankObject; databank-0.19.1/lib/drivers/000077500000000000000000000000001232327675000156025ustar00rootroot00000000000000databank-0.19.1/lib/drivers/caching.js000066400000000000000000000327161232327675000175450ustar00rootroot00000000000000// Databank that uses a cache // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var databank = require('../databank'), Step = require('step'), Databank = databank.Databank, DatabankError = databank.DatabankError, AlreadyExistsError = databank.AlreadyExistsError, NoSuchThingError = databank.NoSuchThingError; var CachingDatabank = function(params) { if (!params.cache) { params.cache = {driver: 'memory', params: {}}; } if (!params.source) { throw new Error("Must define a source."); } this.schema = params.schema || {}; params.source.params.schema = params.cache.params.schema = this.schema; this.cache = Databank.get(params.cache.driver, params.cache.params); this.source = Databank.get(params.source.driver, params.source.params); }; CachingDatabank.prototype = new Databank(); // Connect yourself on up. // params: object containing any params you need // onCompletion(err): function to call on completion CachingDatabank.prototype.connect = function(params, onCompletion) { var bank = this; Step( function() { bank.cache.connect(params.cache, this); }, function(err) { if (err) throw err; bank.source.connect(params.source, this); }, function(err) { onCompletion(err); } ); }; // Disconnect yourself. // onCompletion(err): function to call on completion CachingDatabank.prototype.disconnect = function(onCompletion) { var bank = this; Step( function() { bank.cache.disconnect(this.parallel()); bank.source.disconnect(this.parallel()); }, function(err) { onCompletion(err); } ); }; // Create a new thing // type = string, type of thing, usually 'user' or 'activity' // id = a unique ID, like a nickname or a UUID // value = JavaScript value; will be JSONified // onCompletion(err, value) = function to call on completion CachingDatabank.prototype.create = function(type, id, value, onCompletion) { var bank = this; Step( function() { bank.source.create(type, id, value, this); }, function(err, results) { if (err) throw err; bank.cache.save(type, id, results, this); }, function(err, results) { // XXX: skip if error is from cache? if (err) { onCompletion(err, null); } else { onCompletion(null, results); } } ); }; // Read an existing thing // type = the type of thing; 'user', 'activity' // id = a unique ID -- nickname or UUID or URI // onCompletion(err, value) = function to call on completion CachingDatabank.prototype.read = function(type, id, onCompletion) { var bank = this; Step( function() { bank.cache.read(type, id, this); }, function(err, results) { if (err) { if (err instanceof NoSuchThingError) { // Cache miss bank.source.read(type, id, this); } else { // Some other error onCompletion(err, null); } } else { // Cache hit onCompletion(null, results); } }, function(err, results) { // XXX: make sure to clear cache if not found? if (err) throw err; bank.cache.save(type, id, results, this); }, function(err, results) { // XXX: skip if error is from cache? if (err) { onCompletion(err, null); } else { onCompletion(null, results); } } ); }; // Update an existing thing // type = the type of thing; 'user', 'activity' // id = a unique ID -- nickname or UUID or URI // value = the new value of the thing // onCompletion(err, value) = function to call on completion CachingDatabank.prototype.update = function(type, id, value, onCompletion) { var bank = this; Step( function() { bank.source.update(type, id, value, this); }, function(err, results) { // XXX: make sure to clear cache if not found? if (err) throw err; bank.cache.save(type, id, results, this); }, function(err, results) { // XXX: skip if error is from cache? if (err) { onCompletion(err, null); } else { onCompletion(null, results); } } ); }; // Delete an existing thing // type = the type of thing; 'user', 'activity' // id = a unique ID -- nickname or UUID or URI // value = the new value of the thing // onCompletion(err) = function to call on completion CachingDatabank.prototype.del = function(type, id, onCompletion) { var bank = this; Step( function() { var scall = this.parallel(), ccall = this.parallel(); bank.source.del(type, id, scall); bank.cache.del(type, id, function(err) { if (err && err.name != "NoSuchThingError") { ccall(err); } else { ccall(null); } }); }, function(err) { onCompletion(err); } ); }; // Search for things // type = type of thing // criteria = map of criteria, with exact matches, // like {'subject.id' ='tag =example.org,2011 =evan' } // onResult(value) = called once per result found // onCompletion(err) = called once at the end of results CachingDatabank.prototype.search = function(type, criteria, onResult, onCompletion) { // XXX: is there an efficient way to cache this stuff? this.source.search(type, criteria, onResult, onCompletion); }; // Scan a type // type = type of thing // onResult(value) = called once per result found // onCompletion(err) = called once at the end of results CachingDatabank.prototype.scan = function(type, onResult, onCompletion) { // XXX: Any other choice? Scan cache first, then skip cached stuff from source...? this.source.scan(type, onResult, onCompletion); }; // Update an existing thing // type = the type of thing; 'user', 'activity' // id = a unique ID -- nickname or UUID or URI // value = the new value of the thing // onCompletion(err, value) = function to call on completion CachingDatabank.prototype.save = function(type, id, value, onCompletion) { var bank = this; Step( function() { bank.source.save(type, id, value, this); }, function(err, results) { // XXX: make sure to clear cache if not found? if (err) throw err; bank.cache.save(type, id, results, this); }, function(err, results) { // XXX: skip if error is from cache? if (err) { onCompletion(err, null); } else { onCompletion(null, results); } } ); }; // Read a bunch of things from the db // type = the type of thing; 'user', 'activity' // id = array of IDs // onCompletion(err, results) = function to call on completion; // err = an error or null if none // results = map of id (maybe stringified) to value CachingDatabank.prototype.readAll = function(type, ids, onCompletion) { var bank = this, allMap = {}, notFound = []; Step( function() { bank.cache.readAll(type, ids, this); }, function(err, cacheMap) { var i, id, value; // XXX: make sure to clear cache if not found? if (err) throw err; for (i = 0; i < ids.length; i++) { id = ids[i]; // copy the hits if (cacheMap.hasOwnProperty(id)) { if (cacheMap[id] === null) { notFound.push(id); } else { allMap[id] = cacheMap[id]; } } } if (notFound.length === 0) { onCompletion(null, allMap); } else { bank.source.readAll(type, notFound, this); } }, function(err, sourceMap) { var i, id; if (err) { onCompletion(err, null); } else { for (i = 0; i < ids.length; i++) { id = ids[i]; // copy everything if (sourceMap.hasOwnProperty(id)) { allMap[id] = sourceMap[id]; } } onCompletion(null, allMap); } } ); }; CachingDatabank.prototype.incr = function(type, id, onCompletion) { var bank = this; Step( function() { bank.source.incr(type, id, this); }, function(err, value) { if (err) throw err; bank.cache.save(type, id, value, this); }, function(err, value) { if (err) { onCompletion(err, null); } else { onCompletion(null, value); } } ); }; CachingDatabank.prototype.decr = function(type, id, onCompletion) { var bank = this; Step( function() { bank.source.decr(type, id, this); }, function(err, value) { if (err) throw err; bank.cache.save(type, id, value, this); }, function(err, value) { if (err) { onCompletion(err, null); } else { onCompletion(null, value); } } ); }; CachingDatabank.prototype.append = function(type, id, toAppend, onCompletion) { var bank = this; Step( function() { bank.source.append(type, id, toAppend, this); }, function(err) { var cb = this; if (err) throw err; bank.cache.del(type, id, function(err) { if (err) { if (err.name == 'NoSuchThingError') { cb(null); } else { cb(err); } } else { cb(null); } }); }, onCompletion ); }; CachingDatabank.prototype.prepend = function(type, id, toPrepend, onCompletion) { var bank = this; Step( function() { bank.source.prepend(type, id, toPrepend, this); }, function(err) { var cb = this; if (err) throw err; bank.cache.del(type, id, function(err) { if (err) { if (err.name == 'NoSuchThingError') { cb(null); } else { cb(err); } } else { cb(null); } }); }, onCompletion ); }; CachingDatabank.prototype.item = function(type, id, index, onCompletion) { var bank = this, item = null; Step( function() { bank.cache.item(type, id, index, this); }, function(err, value) { if (err) { if (err instanceof NoSuchThingError) { // Cache miss; read it all and cache bank.source.read(type, id, this); } else { // Some other error onCompletion(err, null); } } else { onCompletion(null, value); } }, function(err, full) { if (err) throw err; item = full[index]; bank.cache.save(type, id, full, this); }, function(err, value) { if (err) { onCompletion(err, null); } else { onCompletion(null, item); } } ); }; CachingDatabank.prototype.slice = function(type, id, start, length, onCompletion) { var bank = this, slice = null; Step( function() { bank.cache.slice(type, id, start, length, this); }, function(err, value) { if (err) { if (err instanceof NoSuchThingError) { // Cache miss; read it all and cache bank.source.read(type, id, this); } else { // Some other error onCompletion(err, null); } } else { onCompletion(null, value); } }, function(err, full) { if (err) throw err; slice = full.slice(start, length); bank.cache.save(type, id, full, this); }, function(err, value) { if (err) { onCompletion(err, null); } else { onCompletion(null, slice); } } ); }; module.exports = CachingDatabank; databank-0.19.1/lib/drivers/memory.js000066400000000000000000000321711232327675000174540ustar00rootroot00000000000000// memory.js // // In-memory storage of data // // Copyright 2011,2012 E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var Step = require('step'), databank = require('../databank'), si = require('set-immediate'), Databank = databank.Databank, DatabankError = databank.DatabankError, AlreadyExistsError = databank.AlreadyExistsError, NoSuchThingError = databank.NoSuchThingError, AlreadyConnectedError = databank.AlreadyConnectedError, NotConnectedError = databank.NotConnectedError; var MemoryDatabank = function(params) { this.schema = params.schema || {}; this.data = params.data || {}; this.connected = false; }; MemoryDatabank.prototype = new Databank(); MemoryDatabank.prototype.freeze = function(value) { var dup, i; switch (typeof value) { case 'number': return value; break; case 'object': if (value instanceof Array) { dup = new Array(value.length); for (i = 0; i < value.length; i++) { dup[i] = this.freeze(value[i]); } return dup; } // FALL THROUGH default: return JSON.stringify(value); } }; MemoryDatabank.prototype.melt = function(value) { var dup, i; switch (typeof value) { case 'string': return JSON.parse(value); break; case 'number': return value; break; case 'object': if (value instanceof Array) { dup = new Array(value.length); for (i = 0; i < value.length; i++) { dup[i] = this.melt(value[i]); } return dup; } // FALL THROUGH default: throw new DatabankError("melt() argument not string, number, or array."); } }; MemoryDatabank.prototype.connect = function(params, onCompletion) { var bank = this; if (bank.connected) { onCompletion(new AlreadyConnectedError()); return; } setImmediate(function() { var type; bank.types = {}; for (type in bank.schema) { bank.types[type] = {}; } bank.connected = true; Step( function() { var key, group = this.group(); for (type in bank.data) { for (key in bank.data[type]) { bank.create(type, key, bank.data[type][key], group()); } } }, function(err, created) { if (err) { onCompletion(err); } else { onCompletion(null); } } ); }); }; MemoryDatabank.prototype.disconnect = function(onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { delete bank.types; // Always succeed bank.connected = false; onCompletion(null); }); }; MemoryDatabank.prototype.create = function(type, id, value, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (id in bank.types[type]) { onCompletion(new AlreadyExistsError(type, id), null); } else { bank.types[type][id] = bank.freeze(value); onCompletion(null, value); } }); }; MemoryDatabank.prototype.update = function(type, id, value, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { onCompletion(new NoSuchThingError(type, id), null); } else { bank.types[type][id] = bank.freeze(value); onCompletion(null, value); } }); }; MemoryDatabank.prototype.read = function(type, id, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { var value; if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { onCompletion(new NoSuchThingError(type, id), null); } else { value = bank.melt(bank.types[type][id]); onCompletion(null, value); } }); }; MemoryDatabank.prototype.del = function(type, id, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { onCompletion(new NoSuchThingError(type, id)); } else { delete bank.types[type][id]; onCompletion(null); } }); }; MemoryDatabank.prototype.save = function(type, id, value, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } bank.types[type][id] = bank.freeze(value); onCompletion(null, value); }); }; MemoryDatabank.prototype.search = function(type, criteria, onResult, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } // We have no indices, so search == scan bank.scan(type, function(value) { if (bank.matchesCriteria(value, criteria)) { onResult(value); } }, function(err) { onCompletion(err); }); }; MemoryDatabank.prototype.scan = function(type, onResult, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { var id, value; if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } for (id in bank.types[type]) { value = bank.melt(bank.types[type][id]); onResult(value); } onCompletion(null); }); }; MemoryDatabank.prototype.readAll = function(type, ids, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { var i, id, value, map = {}; if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } for (i = 0; i < ids.length; i++) { id = ids[i]; if (!(id in bank.types[type])) { map[id] = null; } else { map[id] = bank.melt(bank.types[type][id]); } } onCompletion(null, map); }); }; MemoryDatabank.prototype.incr = function(type, id, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { bank.types[type][id] = 1; onCompletion(null, 1); } else { bank.types[type][id]++; onCompletion(null, bank.types[type][id]); } }); }; MemoryDatabank.prototype.decr = function(type, id, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { bank.types[type][id] = -1; onCompletion(null, -1); } else { bank.types[type][id]--; onCompletion(null, bank.types[type][id]); } }); }; MemoryDatabank.prototype.append = function(type, id, toAppend, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { bank.types[type][id] = [bank.freeze(toAppend)]; } else { bank.types[type][id].push(bank.freeze(toAppend)); } onCompletion(null); }); }; MemoryDatabank.prototype.prepend = function(type, id, toAppend, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { bank.types[type][id] = [bank.freeze(toAppend)]; } else { bank.types[type][id].unshift(bank.freeze(toAppend)); } onCompletion(null); }); }; MemoryDatabank.prototype.item = function(type, id, index, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { onCompletion(new NoSuchThingError(type, id), null); } else if (!(index in bank.types[type][id])) { onCompletion(new DatabankError("Bad index: " + index), null); } else { onCompletion(null, bank.melt(bank.types[type][id][index])); } }); }; MemoryDatabank.prototype.slice = function(type, id, start, length, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { onCompletion(new NoSuchThingError(type, id), null); } else { onCompletion(null, bank.melt(bank.types[type][id].slice(start, length))); } }); }; MemoryDatabank.prototype.indexOf = function(type, id, item, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { onCompletion(new NoSuchThingError(type, id), null); } else { onCompletion(null, bank.types[type][id].indexOf(bank.freeze(item))); } }); }; MemoryDatabank.prototype.remove = function(type, id, item, onCompletion) { var bank = this; if (!bank.connected) { onCompletion(new NotConnectedError()); return; } setImmediate(function() { var i; if (!bank.types) { bank.types = {}; } if (!(type in bank.types)) { bank.types[type] = {}; } if (!(id in bank.types[type])) { onCompletion(new NoSuchThingError(type, id), null); } else { i = bank.types[type][id].indexOf(bank.freeze(item)); if (i === -1) { onCompletion(new DatabankError("No such item")); } else { bank.types[type][id].splice(i, 1); onCompletion(null); } } }); }; module.exports = MemoryDatabank; databank-0.19.1/lib/drivers/partitioning.js000066400000000000000000000132301232327675000206460ustar00rootroot00000000000000// partitioning.js // // A driver for partitioned databanks // // Copyright 2013, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. require('set-immediate'); var databank = require('../databank'), Step = require('step'), EventEmitter = require('events').EventEmitter, Databank = databank.Databank, DatabankError = databank.DatabankError, AlreadyExistsError = databank.AlreadyExistsError, NoSuchThingError = databank.NoSuchThingError, NotConnectedError = databank.NotConnectedError, AlreadyConnectedError = databank.AlreadyConnectedError; var PartitioningDatabank = function(params) { var bank = this, banks = {}, connected = false, typeConnected = new EventEmitter(), getBank = function(type, callback) { var tb, key; if (params[type]) { key = type; } else if (params['*']) { key = '*'; } else { callback(new Error("No bank for type: " + type)); } if (banks[key]) { if (typeConnected.connected[key]) { callback(null, banks[key]); } else { typeConnected.on("connect:"+key, function() { callback(null, banks[key]); }); typeConnected.on("error:"+key, function(err) { callback(err, null); }); } } else { if (params.schema) { if (!params[key].params) { params[key].params = {}; } params[key].params.schema = params.schema; } banks[key] = Databank.get(params[key].driver, params[key].params); if (!typeConnected.connected) { typeConnected.connected = {}; } typeConnected.connected[key] = false; banks[key].connect(params[key].params, function(err) { if (err) { typeConnected.emit("error:"+key, err); callback(err, null); } else { typeConnected.connected[key] = true; setImmediate(function() { callback(null, banks[key]); }); typeConnected.emit("connect:"+key); } }); } }, wrapped = function(name) { return function(type) { var callback = arguments[arguments.length - 1], mainArgs = arguments; if (!connected) { callback(new NotConnectedError()); return; } Step( function() { getBank(type, this); }, function(err, typeBank) { if (err) { callback(err); } else if (!typeBank[name]) { callback(new Error("No method " + name + " for databank for type: " + type)); } else { typeBank[name].apply(typeBank, mainArgs); } } ); }; }, methods = ["create", "read", "update", "del", "search", "scan", "save", "readAll", "incr", "decr", "append", "prepend", "item", "slice", "indexOf", "remove"]; if (params.schema) { bank.schema = params.schema; } bank.connect = function(params, callback) { if (connected) { callback(new AlreadyConnectedError()); return; } connected = true; // We do actual connections when they're needed, so just skip here setImmediate(function() { callback(null); }); }; bank.disconnect = function(callback) { if (!connected) { callback(new NotConnectedError()); return; } connected = false; Step( function() { var group = this.group(), type; for (type in banks) { if (banks.hasOwnProperty(type)) { typeConnected.connected[type] = false; banks[type].disconnect(group()); } } }, function(err) { if (err) { callback(err); } else { callback(null); } } ); }; for (var i = 0; i < methods.length; i++) { bank[methods[i]] = wrapped(methods[i]); } }; PartitioningDatabank.prototype = new Databank(); module.exports = PartitioningDatabank; databank-0.19.1/lib/drivertest.js000066400000000000000000000031171232327675000166570ustar00rootroot00000000000000// drivertest.js // // Builds a test suite based on a given databank driver // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var vows = require('vows'); var tests = [ 'array', 'bank-instance', 'basic-crud', 'connection', 'extreme-ids', 'float', 'integer', 'key-error', 'object-create-hook', 'object-del-hook', 'object-get-hook', 'object-readall-hook', 'object-readall', 'object-readarray-hook', 'object-readarray', 'object-save-hook', 'object-scan', 'object', 'object-update-hook', 'object-delete-and-search', 'object-delete-property-on-save', 'readall', 'save', 'scan', 'search' ]; var DriverTest = function(driver, params) { var suite = vows.describe(driver + 'databank driver test'), maker, i, ctx; for (i = 0; i < tests.length; i++) { maker = require('./test/'+tests[i]); ctx = {}; ctx[tests[i]] = maker(driver, params); suite.addBatch(ctx); } return suite; }; exports.DriverTest = DriverTest; databank-0.19.1/lib/index.js000066400000000000000000000026761232327675000156040ustar00rootroot00000000000000// index.js // // Main export for this directory // // Copyright 2011,2012 E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var databank = require('./databank'), databankobject = require('./databankobject'); exports.Databank = databank.Databank; exports.DatabankError = databank.DatabankError; exports.NotImplementedError = databank.NotImplementedError; exports.NoSuchThingError = databank.NoSuchThingError; exports.AlreadyExistsError = databank.AlreadyExistsError; exports.AlreadyConnectedError = databank.AlreadyConnectedError; exports.NotConnectedError = databank.NotConnectedError; exports.NoSuchItemError = databank.NoSuchItemError; exports.WrongTypeError = databank.WrongTypeError; exports.DatabankObject = databankobject.DatabankObject; // We only want to load this if needed exports.DriverTest = function(driver, params) { var DriverTest = require("./drivertest").DriverTest; return DriverTest(driver, params); };databank-0.19.1/lib/test/000077500000000000000000000000001232327675000151035ustar00rootroot00000000000000databank-0.19.1/lib/test/array.js000066400000000000000000000230641232327675000165640ustar00rootroot00000000000000// test/array.js // // Builds a test context for arrays // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), si = require('set-immediate'), databank = require('../databank'), Databank = databank.Databank; var arrayContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.inbox = { pkey: 'username' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; // Workaround for vows bug setImmediate(function() { bank.disconnect(function(err) { callback(); }); }); }, 'without an error': function(err) { assert.ifError(err); }, 'and we append to an uninitialized array': { topic: function(bank) { bank.append('shoes', 'ericm', {size: 14, color: "blue"}, this.callback); }, teardown: function(bank) { if (bank && bank.del) { bank.del('shoes', 'ericm', function(err) {}); } }, "it works": function(err) { assert.ifError(err); }, "and we fetch the array": { topic: function(bank) { bank.read('shoes', 'ericm', this.callback); }, "it has the right data": function(err, shoes) { assert.ifError(err); assert.isArray(shoes); assert.lengthOf(shoes, 1); assert.isObject(shoes[0]); assert.equal(shoes[0].size, 14); assert.equal(shoes[0].color, "blue"); } } }, 'and we prepend to an uninitialized array': { topic: function(bank) { bank.prepend('friends', 'horaceq', "ericm", this.callback); }, teardown: function(bank) { if (bank && bank.del) { bank.del('friends', 'horaceq', function(err) {}); } }, "it works": function(err) { assert.ifError(err); }, "and we fetch the array": { topic: function(bank) { bank.read('friends', 'horaceq', this.callback); }, "it has the right data": function(err, inbox) { assert.ifError(err); assert.isArray(inbox); assert.lengthOf(inbox, 1); assert.equal(inbox[0], "ericm"); } } }, 'and we can insert an array': { topic: function(bank) { bank.create('inbox', 'evanp', [1, 2, 3], this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isArray(value); assert.equal(value.length, 3); assert.deepEqual(value, [1, 2, 3]); }, 'and we can fetch it': { topic: function(created, bank) { bank.read('inbox', 'evanp', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isArray(value); assert.equal(value.length, 3); assert.deepEqual(value, [1, 2, 3]); }, 'and we can update it': { topic: function(read, created, bank) { bank.update('inbox', 'evanp', [1, 2, 3, 4], this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isArray(value); assert.equal(value.length, 4); assert.deepEqual(value, [1, 2, 3, 4]); }, 'and we can read it again': { topic: function(updated, read, created, bank) { bank.read('inbox', 'evanp', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isArray(value); assert.equal(value.length, 4); assert.deepEqual(value, [1, 2, 3, 4]); }, 'and we can prepend to it': { topic: function(readAgain, updated, read, created, bank) { bank.prepend('inbox', 'evanp', 0, this.callback); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can append to it': { topic: function(readAgain, updated, read, created, bank) { bank.append('inbox', 'evanp', 5, this.callback); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can get a single item': { topic: function(readAgain, updated, read, created, bank) { bank.item('inbox', 'evanp', 2, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.equal(value, 2); }, 'and we can get a slice': { topic: function(item, readAgain, updated, read, created, bank) { bank.slice('inbox', 'evanp', 1, 3, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isArray(value); assert.equal(value.length, 2); assert.deepEqual(value, [1, 2]); }, 'and we can get the indexOf an item': { topic: function(slice, item, readAgain, updated, read, created, bank) { bank.indexOf('inbox', 'evanp', 2, this.callback); }, 'without an error': function(err, index) { assert.ifError(err); assert.equal(index, 2); }, 'and we can remove an item': { topic: function(index, slice, item, readAgain, updated, read, created, bank) { bank.remove('inbox', 'evanp', 3, this.callback); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can read again': { topic: function(index, slice, item, readAgain, updated, read, created, bank) { bank.read('inbox', 'evanp', this.callback); }, 'without an error': function(err, box) { assert.ifError(err); assert.deepEqual(box, [0, 1, 2, 4, 5]); }, 'and we can delete it': { topic: function(readAgainAgain, index, slice, item, readAgain, updated, read, created, bank) { bank.del('inbox', 'evanp', this.callback); }, 'without an error': function(err) { assert.ifError(err); } } } } } } } } } } } } } } }; return context; }; module.exports = arrayContext; databank-0.19.1/lib/test/bank-instance.js000066400000000000000000000052431232327675000201620ustar00rootroot00000000000000// test/bank-instance.js // // Builds a test context for new databank instances // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), databank = require('../databank'), Databank = databank.Databank; var bankInstanceContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { return Databank.get(driver, params); }, 'it is a databank': function(bank) { assert.isObject(bank); assert.instanceOf(bank, Databank); }, 'it has a connect method': function(bank) { assert.isFunction(bank.connect); }, 'it has a disconnect method': function(bank) { assert.isFunction(bank.disconnect); }, 'it has a create method': function(bank) { assert.isFunction(bank.create); }, 'it has a read method': function(bank) { assert.isFunction(bank.read); }, 'it has a update method': function(bank) { assert.isFunction(bank.update); }, 'it has a del method': function(bank) { assert.isFunction(bank.del); }, 'it has a save method': function(bank) { assert.isFunction(bank.save); }, 'it has a readAll method': function(bank) { assert.isFunction(bank.readAll); }, 'it has an incr method': function(bank) { assert.isFunction(bank.incr); }, 'it has a decr method': function(bank) { assert.isFunction(bank.decr); }, 'it has an append method': function(bank) { assert.isFunction(bank.append); }, 'it has a prepend method': function(bank) { assert.isFunction(bank.prepend); }, 'it has an item method': function(bank) { assert.isFunction(bank.item); }, 'it has a slice method': function(bank) { assert.isFunction(bank.slice); } }; return context; }; module.exports = bankInstanceContext; databank-0.19.1/lib/test/basic-crud.js000066400000000000000000000107171232327675000174630ustar00rootroot00000000000000// test/basic-crud.js // // Builds a test context for basic CRUD functionality for a databank // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), si = require('set-immediate'), databank = require('../databank'), Databank = databank.Databank; var basicCrudContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.test = { pkey: 'number' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; // Workaround for vows bug setImmediate(function() { bank.disconnect(function(err) { callback(); }); }); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can create an item': { topic: function(bank) { bank.create('test', 1, {'pass': true, 'iters': 42}, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); }, 'with an object return value': function(err, value) { assert.ifError(err); assert.isObject(value); }, 'with a valid value': function(err, value) { assert.ifError(err); assert.isObject(value); assert.equal(value.pass, true); assert.equal(value.iters, 42); }, 'and we can read it back from the databank': { topic: function(created, bank) { bank.read('test', 1, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); }, 'with an object return value': function(err, value) { assert.isObject(value); }, 'with a valid value': function(err, value) { assert.equal(value.pass, true); assert.equal(value.iters, 42); }, 'and we can update it in the databank': { topic: function(read, created, bank) { bank.update('test', 1, {'pass': true, 'iters': 43}, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); }, 'with an object return value': function(err, value) { assert.isObject(value); }, 'with an updated value': function(err, value) { assert.equal(value.pass, true); assert.equal(value.iters, 43); }, 'and we can delete the entry': { topic: function(updated, read, created, bank) { bank.del('test', 1, this.callback); }, 'without an error': function(err) { assert.ifError(err); } } } } } } }; return context; }; module.exports = basicCrudContext; databank-0.19.1/lib/test/connection.js000066400000000000000000000167261232327675000176140ustar00rootroot00000000000000// test/connection.js // // Tests connect()/disconnect()/NotConnectedError // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank, NotConnectedError = databank.NotConnectedError, AlreadyConnectedError = databank.AlreadyConnectedError; var connectContext = function(driver, params) { var context = {}; context["With the " + driver + " driver"] = { "when we connect a bank": { topic: function() { var bank = Databank.get(driver, params), cb = this.callback; bank.connect({}, function(err) { cb(err, bank); }); }, "it works": function(err, bank) { assert.ifError(err); assert.isObject(bank); }, "and we disconnect the bank": { "topic": function(bank) { bank.disconnect(this.callback); }, "it works": function(err) { assert.ifError(err); } } }, "when we create a value without connecting": { topic: function() { var bank = Databank.get(driver, params), cb = this.callback; bank.create("cellphone", "galaxy-nexus-III", {}, function(err, value) { if (err instanceof NotConnectedError) { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, "it fails with a NotConnected error": function(err) { assert.ifError(err); } }, "when we read a value without connecting": { topic: function() { var bank = Databank.get(driver, params), cb = this.callback; bank.read("cellphone", "iphone", function(err, value) { if (err instanceof NotConnectedError) { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, "it fails with a NotConnected error": function(err) { assert.ifError(err); } }, "when we update a value without connecting": { topic: function() { var bank = Databank.get(driver, params), cb = this.callback; bank.update("cellphone", "palmpre", {}, function(err, value) { if (err instanceof NotConnectedError) { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, "it fails with a NotConnected error": function(err) { assert.ifError(err); } }, "when we delete a value without connecting": { topic: function() { var bank = Databank.get(driver, params), cb = this.callback; bank.del("cellphone", "blackberry", function(err, value) { if (err instanceof NotConnectedError) { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, "it fails with a NotConnected error": function(err) { assert.ifError(err); } }, "when we disconnect from a bank that's not connected": { topic: function() { var bank = Databank.get(driver, params), cb = this.callback; bank.disconnect(function(err) { if (err instanceof NotConnectedError) { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, "it fails with a NotConnected error": function(err) { assert.ifError(err); } }, "when we try to use a bank after disconnecting": { topic: function() { var bank = Databank.get(driver, params), cb = this.callback; Step( function() { bank.connect({}, this); }, function(err) { if (err) throw err; bank.disconnect(this); }, function(err) { var tcb = this; if (err) throw err; bank.create("cellphone", "freerunner", {}, function(err, value) { if (err instanceof NotConnectedError) { tcb(null); } else if (err) { tcb(err); } else { tcb(new Error("Unexpected success")); } }); }, cb ); }, "it fails with a NotConnected error": function(err) { assert.ifError(err); } }, "when we try to connect twice": { topic: function() { var bank = Databank.get(driver, params), cb = this.callback; Step( function() { bank.connect({}, this); }, function(err) { var tcb = this; if (err) throw err; bank.connect({}, function(err) { if (err instanceof AlreadyConnectedError) { tcb(null); } else if (err) { tcb(err); } else { tcb(new Error("Unexpected success")); } }); }, cb ); }, "it fails with an AlreadyConnected error": function(err) { assert.ifError(err); } } }; return context; }; module.exports = connectContext; databank-0.19.1/lib/test/extreme-ids.js000066400000000000000000000070161232327675000176730ustar00rootroot00000000000000// test/extreme-ids.js // // Builds a test context for unusual but legal ID values // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank; var basicCrudContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.locality = { pkey: 'name' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; Step( function() { var i, group = this.group(), items = [ 'The Hague', "Hale'iwa", "Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch" ]; for (i = 0; i < items.length; i++) { bank.del('locality', items[i], group()); } }, function(err) { if (err) throw err; bank.disconnect(this); }, callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can create an item with a space in the id': { topic: function(bank) { bank.create('locality', 'The Hague', {country: 'nl'}, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); } }, 'and we can create an item with punctuation in the id': { topic: function(bank) { bank.create('locality', "Hale'iwa", {country: 'us'}, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); } }, 'and we can create an item with a very long id': { topic: function(bank) { bank.create('locality', "Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch", {country: 'uk'}, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); } } } }; return context; }; module.exports = basicCrudContext; databank-0.19.1/lib/test/float.js000066400000000000000000000140571232327675000165550ustar00rootroot00000000000000// test/float.js // // Test CRUD for floating-point scalars // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), si = require('set-immediate'), databank = require('../databank'), Databank = databank.Databank; var floatContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.probability = { pkey: 'weather' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, 'without an error': function(err) { assert.ifError(err); }, teardown: function(bank) { var callback = this.callback; // Workaround for vows bug setImmediate(function() { bank.disconnect(function(err) { callback(); }); }); }, 'and we can insert a number': { topic: function(bank) { bank.create('probability', 'rain', 0.30, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value - 0.30 < 0.0001, true); }, 'and we can fetch it': { topic: function(created, bank) { bank.read('probability', 'rain', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value - 0.30 < 0.0001, true); }, 'and we can update it': { topic: function(read, created, bank) { bank.update('probability', 'rain', 0.45, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value - 0.45 < 0.0001, true); }, 'and we can read it again': { topic: function(updated, read, created, bank) { bank.read('probability', 'rain', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value - 0.45 < 0.0001, true); }, 'and we can save it': { topic: function(readAgain, updated, read, created, bank) { bank.save('probability', 'rain', 0.25, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value - 0.25 < 0.0001, true); }, 'and we can read it once more': { topic: function(saved, readAgain, updated, read, created, bank) { bank.read('probability', 'rain', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value - 0.45 < 0.0001, true); }, 'and we can delete it': { topic: function(readOnceMore, saved, readAgain, updated, read, created, bank) { bank.del('probability', 'rain', this.callback); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can save to create': { topic: function(readOnceMore, saved, readAgain, updated, read, created, bank) { bank.save('probability', 'fog', 0.10, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value - 0.10 < 0.0001, true); }, 'and we can delete it': { topic: function(saveCreated, readOnceMore, saved, readAgain, updated, read, created, bank) { bank.del('probability', 'fog', this.callback); }, 'without an error': function(err) { assert.ifError(err); } } } } } } } } } } } }; return context; }; module.exports = floatContext; databank-0.19.1/lib/test/integer.js000066400000000000000000000126311232327675000171010ustar00rootroot00000000000000// test/integer.js // // Test CRUD for integer scalars // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), si = require('set-immediate'), databank = require('../databank'), Databank = databank.Databank; var integerContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema['computer-count'] = { pkey: 'username' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; // Workaround for vows bug setImmediate(function() { bank.disconnect(function(err) { callback(); }); }); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can insert an integer': { topic: function(bank) { bank.create('computer-count', 'evanp', 3, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value, 3); }, 'and we can fetch it': { topic: function(created, bank) { bank.read('computer-count', 'evanp', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value, 3); }, 'and we can update it': { topic: function(read, created, bank) { bank.update('computer-count', 'evanp', 5, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value, 5); }, 'and we can read it again': { topic: function(updated, read, created, bank) { bank.read('computer-count', 'evanp', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value, 5); }, 'and we can increment it': { topic: function(readAgain, updated, read, created, bank) { bank.incr('computer-count', 'evanp', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value, 6); }, 'and we can decrement it': { topic: function(incremented, readAgain, updated, read, created, bank) { bank.decr('computer-count', 'evanp', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isNumber(value); assert.equal(value, 5); }, 'and we can delete it': { topic: function(decremented, incremented, readAgain, updated, read, created, bank) { bank.del('computer-count', 'evanp', this.callback); }, 'without an error': function(err) { assert.ifError(err); } } } } } } } } } }; return context; }; module.exports = integerContext; databank-0.19.1/lib/test/key-error.js000066400000000000000000000133541232327675000173660ustar00rootroot00000000000000// test/key-error.js // // Testing for key errors // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank, NoSuchThingError = databank.NoSuchThingError, AlreadyExistsError = databank.AlreadyExistsError; var keyErrorContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.painting = { pkey: 'title', fields: ['artist', 'style'], indices: ['artist'] }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { Step( function() { var i, group = this.group(); bank.del('painting', 'Starry Night', group()); }, function(err) { if (err) throw err; bank.disconnect(this); }, this.callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and when we double-insert a value': { topic: function(bank) { var cb = this.callback, p = {title: 'Starry Night', artist: 'Vincent Van Gogh', style: 'Impressionist'}; Step( function() { bank.create('painting', p.title, p, this); }, function(err, results) { if (err) { cb(err); } else { bank.create('painting', p.title, p, this); } }, function(err, results) { if (err && err.name == "AlreadyExistsError") { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } } ); }, 'we get an AlreadyExistsError': function(err) { assert.ifError(err); } }, 'and when we try to read a non-existent value': { topic: function(bank) { var cb = this.callback; bank.read('painting', 'Mona Lisa', function(err, value) { if (err && err.name == "NoSuchThingError") { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, 'we get a NoSuchThingError': function(err) { assert.ifError(err); } }, 'and when we try to update a non-existent value': { topic: function(bank) { var cb = this.callback, p = {title: 'Number 1', artist: 'Jackson Pollock', style: 'Abstract Expressionist'}; bank.update('painting', p.title, p, function(err, value) { if (err && err.name == "NoSuchThingError") { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, 'we get a NoSuchThingError': function(err) { assert.ifError(err); } }, 'and when we try to delete a non-existent value': { topic: function(bank) { var cb = this.callback; bank.del('painting', 'Landscape with the Fall of Icarus', function(err, value) { if (err && err.name == "NoSuchThingError") { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, 'we get a NoSuchThingError': function(err) { assert.ifError(err); } } } }; return context; }; module.exports = keyErrorContext; databank-0.19.1/lib/test/object-create-hook.js000066400000000000000000000115451232327675000211140ustar00rootroot00000000000000// test/object-create-hook.js // // Test create hooks for Databank objects // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank, DatabankObject = require('../databankobject').DatabankObject; var objectCreateHookContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.album = { pkey: 'title' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; Step( function() { bank.del('album', "Who's Next", this); }, function(err) { if (err) throw err; bank.disconnect(this); }, callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the Album class': { topic: function(bank) { var Album = DatabankObject.subClass('album'); Album.bank = function() { return bank; }; return Album; }, 'which is valid': function(Album) { assert.isFunction(Album); }, 'which has default beforeCreate()': function(Album) { assert.isFunction(Album.beforeCreate); }, 'which has default afterCreate()': function(Album) { assert.isFunction(Album.prototype.afterCreate); }, 'and we can create a Album': { topic: function(Album, bank) { var cb = this.callback, called = { before: false, after: false, album: null }; Album.beforeCreate = function(props, callback) { called.before = true; props.addedByBefore = 42; callback(null, props); }; Album.prototype.afterCreate = function(callback) { called.after = true; this.addedByAfter = 23; callback(null, this); }; Album.create({title: "Who's Next"}, function(err, album) { if (err) { cb(err, null); } else { called.album = album; // note: not the album cb(null, called); } }); }, 'without an error': function(err, called) { assert.ifError(err); }, 'and the before hook is called': function(err, called) { assert.isTrue(called.before); }, 'and the after hook is called': function(err, called) { assert.isTrue(called.after); }, 'and the before hook modification happened': function(err, called) { assert.equal(called.album.addedByBefore, 42); }, 'and the after hook modification happened': function(err, called) { assert.equal(called.album.addedByAfter, 23); } } } } }; return context; }; module.exports = objectCreateHookContext; databank-0.19.1/lib/test/object-del-hook.js000066400000000000000000000103301232327675000204040ustar00rootroot00000000000000// test/object-del-hook.js // // Test del hooks for Databank objects // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), si = require('set-immediate'), databank = require('../databank'), Databank = databank.Databank, DatabankObject = require('../databankobject').DatabankObject; var objectDelHookContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.flight = { pkey: 'number' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; // Workaround for vows bug setImmediate(function() { bank.disconnect(function(err) { callback(); }); }); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the Flight class': { topic: function(bank) { var Flight = DatabankObject.subClass('flight'); Flight.bank = function() { return bank; }; return Flight; }, 'which is valid': function(Flight) { assert.isFunction(Flight); }, 'and we can create and delete a Flight': { topic: function(Flight) { var cb = this.callback, called = { before: false, after: false }; Flight.prototype.beforeDel = function(callback) { called.before = true; callback(null); }; Flight.prototype.afterDel = function(callback) { called.after = true; callback(null); }; Flight.create({number: "AC761"}, function(err, flight) { if (err) { cb(err, null); } else { flight.del(function(err) { if (err) { cb(err, null); } else { // note: not the flight cb(null, called); } }); } }); }, 'without an error': function(err, called) { assert.ifError(err); assert.isObject(called); }, 'and the before hook is called': function(err, called) { assert.isTrue(called.before); }, 'and the after hook is called': function(err, called) { assert.isTrue(called.after); } } } } }; return context; }; module.exports = objectDelHookContext; databank-0.19.1/lib/test/object-delete-and-search.js000066400000000000000000000114461232327675000221600ustar00rootroot00000000000000// test/object.js // // Testing DatabankObject basic functionality // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), si = require('set-immediate'), databank = require('../databank'), Step = require('step'), Databank = databank.Databank, NoSuchThingError = databank.NoSuchThingError, DatabankObject = require('../databankobject').DatabankObject; var objectContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.user = { pkey: 'username', fields: ['name'], indices: ['name.last'] }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; // Workaround for vows bug setImmediate(function() { bank.disconnect(function(err) { callback(); }); }); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the User class': { topic: function(bank) { var User = DatabankObject.subClass('user'); User.bank = function() { return bank; }; return User; }, 'which is valid': function(User) { assert.ok(User); }, 'and we create and delete a User': { topic: function(User, bank) { var callback = this.callback, maj = {username: 'maj', name: {last: 'Jenkins', first: 'Michele'}}; Step( function() { User.create(maj, this); }, function(err, maj) { if (err) throw err; maj.del(this); }, callback ); }, "it works": function(err) { assert.ifError(err); }, "and we try to get the User": { topic: function(User, bank) { var callback = this.callback; User.get("maj", function(err, p) { if (err && err instanceof NoSuchThingError) { callback(null); } else if (err) { callback(err); } else { callback(new Error("Unexpected success")); } }); }, "it returns a NoSuchThingError": function(err) { assert.ifError(err); } }, "and we search for the User": { topic: function(User, bank) { User.search({"name.last": "Jenkins"}, this.callback); }, "it works": function(err, results) { assert.ifError(err); assert.isArray(results); }, "there are no results": function(err, results) { assert.ifError(err); assert.isArray(results); assert.lengthOf(results, 0); } } } } } }; return context; }; module.exports = objectContext; databank-0.19.1/lib/test/object-delete-property-on-save.js000066400000000000000000000075331232327675000234070ustar00rootroot00000000000000// test/object-save-hook.js // // Testing DatabankObject save() hooks // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank, DatabankObject = require('../databankobject').DatabankObject; var objectDeletePropertyOnSaveContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.tree = { pkey: 'name' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { Step( function() { bank.del("tree", "oak", this); }, function(err) { if (err) throw err; bank.disconnect(this); }, this.callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the Tree class': { topic: function(bank) { var Tree = DatabankObject.subClass('tree'); Tree.bank = function() { return bank; }; return Tree; }, 'which is valid': function(Tree) { assert.isFunction(Tree); }, 'and we can create a tree': { topic: function(Tree) { Tree.create({name: "oak", type: "deciduous", bark: "rough"}, this.callback); }, "it works": function(err, tree) { assert.ifError(err); assert.isObject(tree); }, "and we delete a property and save()": { topic: function(tree) { delete tree.bark; tree.save(this.callback); }, "it works": function(err, saved) { assert.ifError(err); assert.isObject(saved); }, "and we re-read the object": { topic: function(saved, tree, Tree) { Tree.get("oak", this.callback); }, "it works": function(err, tree) { assert.ifError(err); }, "it does not have the deleted property": function(err, tree) { assert.ifError(err); assert.isUndefined(tree.bark); } } } } } } }; return context; }; module.exports = objectDeletePropertyOnSaveContext; databank-0.19.1/lib/test/object-get-hook.js000066400000000000000000000113271232327675000204260ustar00rootroot00000000000000// test/object-get-hook.js // // Test get hooks for Databank objects // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank, DatabankObject = require('../databankobject').DatabankObject; var objectGetHookContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.stock = { pkey: 'ticker' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; Step( function() { bank.del('stock', "GOOG", this); }, function(err) { if (err) throw err; bank.disconnect(this); }, callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the Stock class': { topic: function(bank) { var Stock = DatabankObject.subClass('stock'); Stock.bank = function() { return bank; }; return Stock; }, 'which is valid': function(Stock) { assert.isFunction(Stock); }, 'and we can create and get a Stock': { topic: function(Stock) { var cb = this.callback, called = { before: false, after: false, stock: null }; Stock.beforeGet = function(ticker, callback) { called.before = true; callback(null, ticker); }; Stock.prototype.afterGet = function(callback) { called.after = true; this.addedByAfter = 23; callback(null, this); }; Stock.create({ticker: "GOOG"}, function(err, stock) { if (err) { cb(err, null); } else { Stock.get("GOOG", function(err, newStock) { if (err) { cb(err, null); } else { called.stock = newStock; // note: not the stock cb(null, called); } }); } }); }, 'without an error': function(err, called) { assert.ifError(err); assert.isObject(called); }, 'and the before hook is called': function(err, called) { assert.isObject(called); assert.isTrue(called.before); }, 'and the after hook is called': function(err, called) { assert.isTrue(called.after); }, 'and the after hook modification happened': function(err, called) { assert.equal(called.stock.addedByAfter, 23); } } } } }; return context; }; module.exports = objectGetHookContext; databank-0.19.1/lib/test/object-readall-hook.js000066400000000000000000000136171232327675000212570ustar00rootroot00000000000000// test/object-readall-hook.js // // Testing DatabankObject readAll() hooks // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), Step = require('step'), vows = require('vows'), databank = require('../databank'), Databank = databank.Databank, DatabankObject = require('../databankobject').DatabankObject, keys = ['1932 Ford V8', '1959 Austin Mini', '1955 Chevrolet', '1938 Volkswagen Beetle', '1964 Porsche 911', '1955 Mercedes-Benz 300SL "Gullwing"']; // http://www.insideline.com/features/the-100-greatest-cars-of-all-time.html var objectReadallHookContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.auto = { pkey: 'yearmakemodel' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; Step( function() { var i, group = this.group(); for (i = 0; i < keys.length; i++) { bank.del('auto', keys[i], group()); } }, function(err) { if (err) { console.error(err); } bank.disconnect(this); }, callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the Auto class': { topic: function(bank) { var Auto; Auto = DatabankObject.subClass('auto'); Auto.bank = function() { return bank; }; return Auto; }, 'which is valid': function(Auto) { assert.isFunction(Auto); }, 'and we can create a few autos': { topic: function(Auto) { var cb = this.callback; Step( function() { var i, group = this.group(); for (i = 0; i < keys.length; i++) { Auto.create({yearmakemodel: keys[i]}, group()); } }, function(err, autos) { cb(err, autos); } ); }, 'it works': function(err, autos) { assert.ifError(err); assert.isArray(autos); }, 'and we read a few back': { topic: function(autos, Auto) { var cb = this.callback, called = { before: 0, after: 0, autos: {} }; Auto.beforeGet = function(yearmakemodel, callback) { called.before++; callback(null, yearmakemodel); }; Auto.prototype.afterGet = function(callback) { called.after++; this.addedByAfter = 23; callback(null, this); }; Auto.readAll(keys, function(err, ppl) { called.autos = ppl; cb(err, called); }); }, 'without an error': function(err, called) { assert.ifError(err); assert.isObject(called); }, 'and the before hook is called': function(err, called) { assert.isObject(called); assert.equal(called.before, keys.length); }, 'and the after hook is called': function(err, called) { assert.equal(called.after, keys.length); }, 'and the after hook modification happened': function(err, called) { var nick; for (nick in called.autos) { assert.isObject(called.autos[nick]); assert.equal(called.autos[nick].addedByAfter, 23); } } } } } } }; return context; }; module.exports = objectReadallHookContext; databank-0.19.1/lib/test/object-readall.js000066400000000000000000000105501232327675000203120ustar00rootroot00000000000000// test/object-readall.js // // Testing DatabankObject readAll() // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var vows = require('vows'), assert = require('assert'), databank = require('../databank'), Databank = databank.Databank, Step = require('step'), DatabankObject = require('../databankobject').DatabankObject; var data = [ {name: "Mercury", moons: 0}, {name: "Venus", moons: 0}, {name: "Earth", moons: 1}, {name: "Mars", moons: 2}, {name: "Jupiter", moons: 66}, {name: "Saturn", moons: 62}, {name: "Uranus", moons: 27}, {name: "Neptune", moons: 13} ]; var ids = ['Venus', 'Mars', 'Saturn', 'invalid']; var objectReadallContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.planet = { pkey: 'name' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, 'without an error': function(err) { assert.ifError(err); }, teardown: function(bank) { var callback = this.callback; Step( function() { var i, group = this.group(); for (i = 0; i < data.length; i++) { bank.del('planet', data[i].name, group()); } }, function(err) { if (err) throw err; bank.disconnect(this); }, callback ); }, 'and we can initialize the Planet class': { topic: function(bank) { var Planet = DatabankObject.subClass('planet'); // Override so there's not a global causing grief. Planet.bank = function() { return bank; }; return Planet; }, 'and we can create some people': { topic: function(Planet, bank) { var that = this; Step( function() { var i, group = this.group(); for (i = 0; i < data.length; i++) { Planet.create(data[i], group()); } }, function(err, planets) { that.callback(err, planets); } ); }, 'without an error': function(err, planets) { assert.ifError(err); }, 'and we can read a few of them': { topic: function(planets, Planet, bank) { Planet.readAll(ids, this.callback); }, 'without an error': function(err, planetMap) { assert.ifError(err); assert.isObject(planetMap); assert.isObject(planetMap.Venus); assert.isObject(planetMap.Mars); assert.isObject(planetMap.Saturn); assert.isNull(planetMap.invalid); } } } } } }; return context; }; module.exports = objectReadallContext; databank-0.19.1/lib/test/object-readarray-hook.js000066400000000000000000000142551232327675000216240ustar00rootroot00000000000000// test/object-readarray-hook.js // // Testing DatabankObject readArray() // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank, DatabankObject = require('../databankobject').DatabankObject, data = [ {name: "blender", action: "blend"}, {name: "dishwasher", action: "wash"}, {name: "toaster", action: "toast"}, {name: "refrigerator", action: "refrigerate"}, {name: "mixer", action: "mix"}, {name: "waffle iron", action: "iron"} ], names = ['blender', 'dishwasher', 'toaster', 'refrigerator', 'mixer', 'waffle iron']; var objectReadArrayHookContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.appliance = { pkey: 'name' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; Step( function() { var i, group = this.group(); for (i = 0; i < data.length; i++) { bank.del("appliance", data[i].name, group()); } }, function(err) { if (err) throw err; bank.disconnect(this); }, callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the Appliance class': { topic: function(bank) { var Appliance; Appliance = DatabankObject.subClass('appliance'); Appliance.bank = function() { return bank; }; return Appliance; }, 'which is valid': function(Appliance) { assert.isFunction(Appliance); }, 'and we can create a few appliances': { topic: function(Appliance) { var cb = this.callback; Step( function() { var i, group = this.group(); for (i = 0; i < data.length; i++) { Appliance.create(data[i], group()); } }, function(err, appliances) { cb(err, appliances); } ); }, 'it works': function(err, appliances) { assert.ifError(err); assert.isArray(appliances); }, 'and we read a few back': { topic: function(appliances, Appliance) { var cb = this.callback, called = { before: 0, after: 0, appliances: {} }; Appliance.beforeGet = function(name, callback) { called.before++; callback(null, name); }; Appliance.prototype.afterGet = function(callback) { called.after++; this.addedByAfter = 23; callback(null, this); }; Appliance.readArray(names, function(err, appl) { called.appliances = appl; cb(err, called); }); }, 'without an error': function(err, called) { assert.ifError(err); assert.isObject(called); }, 'and the before hook is called': function(err, called) { assert.isObject(called); assert.equal(called.before, names.length); }, 'and the after hook is called': function(err, called) { assert.equal(called.after, names.length); }, 'and the after hook modification happened': function(err, called) { var i = 0; assert.isArray(called.appliances); assert.lengthOf(called.appliances, names.length); for (i = 0; i < called.appliances.length; i++) { assert.isObject(called.appliances[i]); assert.equal(called.appliances[i].addedByAfter, 23); } } } } } } }; return context; }; module.exports = objectReadArrayHookContext; databank-0.19.1/lib/test/object-readarray.js000066400000000000000000000105221232327675000206570ustar00rootroot00000000000000// test/object-readarray.js // // Testing DatabankObject readArray() // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var vows = require('vows'), assert = require('assert'), databank = require('../databank'), Databank = databank.Databank, Step = require('step'), DatabankObject = require('../databankobject').DatabankObject; var data = [ {abbr: 'MA', name: 'Massachussetts'}, {abbr: 'CA', name: 'California'}, {abbr: 'NY', name: 'New York'}, {abbr: 'MO', name: 'Missouri'}, {abbr: 'WY', name: 'Wyoming'} ]; var ids = ['CA', 'NY', 'MO', 'invalid']; var objectReadarrayContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.state = { pkey: 'abbr' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; Step( function() { var i, group = this.group(); for (i = 0; i < data.length; i++) { bank.del("state", data[i].abbr, group()); } }, function(err) { if (err) throw err; bank.disconnect(this); }, callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the State class': { topic: function(bank) { var State = DatabankObject.subClass('state'); // Override so there's not a global causing grief. State.bank = function() { return bank; }; return State; }, 'and we can create some states': { topic: function(State, bank) { var cb = this.callback; Step( function() { var i, group = this.group(); for (i = 0; i < data.length; i++) { State.create(data[i], group()); } }, cb ); }, 'without an error': function(err, states) { assert.ifError(err); }, 'and we can read a few of them': { topic: function(states, State, bank) { State.readArray(ids, this.callback); }, 'without an error': function(err, statesArray) { var i; assert.ifError(err); assert.isArray(statesArray); assert.lengthOf(statesArray, ids.length); for (i = 0; i < 3; i++) { assert.isObject(statesArray[i]); assert.equal(statesArray[i].abbr, ids[i]); } assert.isNull(statesArray[3]); } } } } } }; return context; }; module.exports = objectReadarrayContext; databank-0.19.1/lib/test/object-save-hook.js000066400000000000000000000112151232327675000206010ustar00rootroot00000000000000// test/object-save-hook.js // // Testing DatabankObject save() hooks // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank, DatabankObject = require('../databankobject').DatabankObject; var objectSaveHookContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.website = { pkey: 'domain' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { Step( function() { bank.del("website", "example.com", this); }, function(err) { if (err) throw err; bank.disconnect(this); }, this.callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the Website class': { topic: function(bank) { var Website = DatabankObject.subClass('website'); Website.bank = function() { return bank; }; return Website; }, 'which is valid': function(Website) { assert.isFunction(Website); }, 'and we can create and save a Website': { topic: function(Website) { var cb = this.callback, called = { before: false, after: false, website: null }; Website.prototype.beforeSave = function(callback) { called.before = true; this.addedByBefore = 42; callback(null); }; Website.prototype.afterSave = function(callback) { called.after = true; this.addedByAfter = 23; callback(null); }; var website = new Website({domain: "example.com"}); website.save(function(err, newWebsite) { if (err) { cb(err, null); } else { called.website = newWebsite; // note: not the website cb(null, called); } }); }, 'without an error': function(err, called) { assert.ifError(err); assert.isObject(called); }, 'and the before hook is called': function(err, called) { assert.isTrue(called.before); }, 'and the after hook is called': function(err, called) { assert.isTrue(called.after); }, 'and the before hook modification happened': function(err, called) { assert.equal(called.website.addedByBefore, 42); }, 'and the after hook modification happened': function(err, called) { assert.equal(called.website.addedByAfter, 23); } } } } }; return context; }; module.exports = objectSaveHookContext; databank-0.19.1/lib/test/object-scan.js000066400000000000000000000121601232327675000176310ustar00rootroot00000000000000// test/object.js // // Testing DatabankObject scan functionality // // Copyright 2013, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), databank = require('../databank'), Step = require('step'), Databank = databank.Databank, NoSuchThingError = databank.NoSuchThingError, DatabankObject = require('../databankobject').DatabankObject; var data = [ { uuid: "baad21be-8fee-11e2-b812-2c8158efb9e9", name: "Fred Q. Jones", company: "Acme Corporation", phone: "+1-212-555-1001" }, { uuid: "c9fa559c-8fee-11e2-96b3-2c8158efb9e9", name: "Fred W. Jones", company: "General Eclectic", phone: "+1-213-555-1001" }, { uuid: "c9fa9d9a-8fee-11e2-beb1-2c8158efb9e9", name: "Fred N. Jones", company: "Consolidated Amalgamations", phone: "+1-214-555-1001" }, { uuid: "c9faea20-8fee-11e2-97e4-2c8158efb9e9", name: "Fred R. Jones", company: "American Company Corporation", phone: "+1-215-555-1001" }, { uuid: "c9fb37be-8fee-11e2-8472-2c8158efb9e9", name: "Fred B. Jones", company: "The Industry Group", phone: "+1-216-555-1001" } ]; var objectScanContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.card = { pkey: 'uuid', fields: ['name', 'company'] }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; Step( function() { var i, group = this.group(); for (i = 0; i < data.length; i++) { bank.del('card', data[i].imdb, group()); } }, function(err) { if (err) throw err; bank.disconnect(this); }, callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the Card class': { topic: function(bank) { var Card = DatabankObject.subClass('card'); Card.bank = function() { return bank; }; return Card; }, 'which is valid': function(Card) { assert.ok(Card); assert.ok(Card.scan); }, 'and we can add some cards': { topic: function(Card, bank) { var cb = this.callback; Step( function() { var i = 0, group = this.group(); for ( i = 0; i < data.length; i++) { Card.create(data[i], group()); } }, cb ); }, 'which works': function(err, cards) { assert.ifError(err); assert.isArray(cards); assert.equal(cards.length, data.length); }, 'and we scan the Card class': { topic: function(created, Card, bank) { var cb = this.callback, results = [], onResult = function(result) { results.push(result); }; Card.scan(onResult, function(err) { cb(err, results); }); }, 'which is valid': function(err, cards) { assert.ifError(err); assert.isArray(cards); assert.equal(cards.length, data.length); } } } } } }; return context; }; module.exports = objectScanContext; databank-0.19.1/lib/test/object-update-hook.js000066400000000000000000000121041232327675000211230ustar00rootroot00000000000000// test/object-update-hook.js // // Testing DatabankObject update() hooks // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank, DatabankObject = require('../databankobject').DatabankObject; var objectUpdateHookContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.species = { pkey: 'binomial', fields: ['common'] }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { Step( function() { bank.del("species", "Canis lupus", this); }, function(err) { if (err) throw err; bank.disconnect(this); }, this.callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the Species class': { topic: function(bank) { var Species = DatabankObject.subClass('species'); Species.bank = function() { return bank; }; return Species; }, 'which is valid': function(Species) { assert.isFunction(Species); }, 'and we can create and update a Species': { topic: function(Species) { var cb = this.callback, called = { before: false, after: false, species: null }; Species.prototype.beforeUpdate = function(props, callback) { called.before = true; props.addedByBefore = 42; callback(null, props); }; Species.prototype.afterUpdate = function(callback) { called.after = true; this.addedByAfter = 23; callback(null, this); }; Species.create({binomial: "Canis lupus"}, function(err, species) { if (err) { cb(err, null); } else { species.update({binomial: "Canis lupus", common: "Gray wolf"}, function(err, newSpecies) { if (err) { cb(err, null); } else { called.species = newSpecies; // note: not the species cb(null, called); } }); } }); }, 'without an error': function(err, called) { assert.ifError(err); assert.isObject(called); }, 'and the before hook is called': function(err, called) { assert.isObject(called); assert.isTrue(called.before); }, 'and the after hook is called': function(err, called) { assert.isTrue(called.after); }, 'and the before hook modification happened': function(err, called) { assert.equal(called.species.addedByBefore, 42); }, 'and the after hook modification happened': function(err, called) { assert.equal(called.species.addedByAfter, 23); } } } } }; return context; }; module.exports = objectUpdateHookContext; databank-0.19.1/lib/test/object.js000066400000000000000000000167361232327675000167240ustar00rootroot00000000000000// test/object.js // // Testing DatabankObject basic functionality // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), si = require('set-immediate'), databank = require('../databank'), Step = require('step'), Databank = databank.Databank, NoSuchThingError = databank.NoSuchThingError, DatabankObject = require('../databankobject').DatabankObject; var objectContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.person = { pkey: 'username', fields: ['name'], indices: ['name.last'] }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { var callback = this.callback; // Workaround for vows bug setImmediate(function() { bank.disconnect(function(err) { callback(); }); }); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can initialize the Person class': { topic: function(bank) { var Person = DatabankObject.subClass('person'); Person.bank = function() { return bank; }; return Person; }, 'which is valid': function(Person) { assert.ok(Person); assert.ok(Person.get); assert.ok(Person.search); assert.ok(Person.scan); assert.ok(Person.pkey); assert.ok(Person.create); assert.ok(Person.bank()); assert.equal(Person.type, 'person'); assert.equal(Person.pkey(), 'username'); }, 'and we can instantiate a new Person': { topic: function(Person, bank) { return new Person({username: 'evanp', name: {last: 'Prodromou', first: 'Evan'}, age: 42}); }, 'which is valid': function(evan) { assert.ok(evan); assert.ok(evan.del); assert.ok(evan.save); assert.ok(evan.update); assert.equal(evan.username, 'evanp'); assert.ok(evan.name); assert.equal(evan.name.last, 'Prodromou'); assert.equal(evan.name.first, 'Evan'); assert.equal(evan.age, 42); }, 'and we can save that person': { topic: function(evan, Person, bank) { evan.save(this.callback); }, 'which is valid': function(err, person) { assert.ifError(err); assert.ok(person); assert.equal(person.username, 'evanp'); assert.ok(person.name); assert.equal(person.name.last, 'Prodromou'); assert.equal(person.name.first, 'Evan'); assert.equal(person.age, 42); }, 'and we can read that person': { topic: function(saved, evan, Person, bank) { Person.get('evanp', this.callback); }, 'which is valid': function(err, person) { assert.ifError(err); assert.ok(person); assert.equal(person.username, 'evanp'); assert.ok(person.name); assert.equal(person.name.last, 'Prodromou'); assert.equal(person.name.first, 'Evan'); assert.equal(person.age, 42); }, 'and we can save a changed person': { topic: function(person, saved, evan, Person, bank) { person.age = 43; person.save(this.callback); }, 'which is valid': function(err, person) { assert.ifError(err); assert.ok(person); assert.equal(person.username, 'evanp'); assert.ok(person.name); assert.equal(person.name.last, 'Prodromou'); assert.equal(person.name.first, 'Evan'); assert.equal(person.age, 43); }, 'and we can fetch the saved person': { topic: function(changed, read, saved, evan, Person, bank) { Person.get('evanp', this.callback); }, 'which is valid': function(err, person) { assert.ifError(err); assert.ok(person); assert.equal(person.username, 'evanp'); assert.ok(person.name); assert.equal(person.name.last, 'Prodromou'); assert.equal(person.name.first, 'Evan'); assert.equal(person.age, 43); }, 'and we can delete the fetched, saved person': { topic: function(reread, changed, read, saved, evan, Person, bank) { reread.del(this.callback); }, 'without an error': function(err) { assert.ifError(err); } } } } } } } } } }; return context; }; module.exports = objectContext; databank-0.19.1/lib/test/readall.js000066400000000000000000000075461232327675000170610ustar00rootroot00000000000000// test/readall.js // // Testing readAll() method // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank; var readAllContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.task = { pkey: 'number' }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { Step( function() { var i, group = this.group(); for (i = 0; i < 10; i++) { bank.del('task', i, group()); } }, function(err) { if (err) throw err; bank.disconnect(this); }, this.callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can create a bunch of items': { topic: function(bank) { var cb = this.callback; Step( function() { var i, group = this.group(); for (i = 0; i < 10; i++) { bank.create('task', i, {number: i, complete: false, priority: 5}, group()); } }, cb ); }, 'without an error': function(err, tasks) { var i; assert.ifError(err); assert.isArray(tasks); assert.lengthOf(tasks, 10); for (i = 0; i < 10; i++) { assert.isObject(tasks[i]); } }, 'and we can read some back': { topic: function(tasks, bank) { bank.readAll('task', [2, 3, 4, 'nonexistent'], this.callback); }, 'without an error': function(err, results) { assert.ifError(err); }, 'with the correct data': function(err, results) { var i; assert.ifError(err); assert.isObject(results); for (i = 2; i <= 4; i++) { assert.isObject(results[i]); assert.equal(results[i].number, i); assert.isFalse(results[i].complete); assert.equal(results[i].priority, 5); } assert.isNull(results['nonexistent']); } } } } }; return context; }; module.exports = readAllContext; databank-0.19.1/lib/test/save.js000066400000000000000000000113171232327675000164020ustar00rootroot00000000000000// test/save.js // // Testing save() method // // Copyright 2011,2012 E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), databank = require('../databank'), Databank = databank.Databank; var saveContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.grape = { pkey: 'name', fields: ['color', 'flavor'] }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can save an item': { topic: function(bank) { bank.save('grape', 'merlot', {'color': 'red', 'flavor': 'rich'}, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.ok(value); assert.isObject(value); assert.equal(value.color, 'red'); assert.equal(value.flavor, 'rich'); }, 'and we can fetch it': { topic: function(created, bank) { bank.read('grape', 'merlot', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.isObject(value); assert.equal(value.color, 'red'); assert.equal(value.flavor, 'rich'); }, 'and we can save it again': { topic: function(read, created, bank) { bank.save('grape', 'merlot', {'color': 'red', 'flavor': 'lush'}, this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.ok(value); assert.equal(typeof value, 'object'); assert.equal(value.color, 'red'); assert.equal(value.flavor, 'lush'); }, 'and we can read it again': { topic: function(updated, read, created, bank) { bank.read('grape', 'merlot', this.callback); }, 'without an error': function(err, value) { assert.ifError(err); assert.ok(value); assert.equal(typeof value, 'object'); assert.equal(value.color, 'red'); assert.equal(value.flavor, 'lush'); }, 'and we can delete it': { topic: function(readAgain, updated, read, created, bank) { bank.del('grape', 'merlot', this.callback); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can disconnect': { topic: function(readAgain, updated, read, created, bank) { bank.disconnect(this.callback); }, 'without an error': function(err) { assert.ifError(err); } } } } } } } } }; return context; }; module.exports = saveContext; databank-0.19.1/lib/test/scan.js000066400000000000000000000121401232327675000163630ustar00rootroot00000000000000// test/scan.js // // Testing scan() method // // Copyright 2013, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank; // Films featuring prospectors var data = [ { imdb: 'tt0469494', title: 'There Will Be Blood', year: 2007 }, { imdb: 'tt0040897', title: 'The Treasure of the Sierra Madre', year: 1948 }, { imdb: 'tt0064615', title: 'Mackenna\'s Gold', year: 1969 }, { imdb: 'tt0120363', title: 'Toy Story 2', year: 1999 } ]; var scanContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.film = { pkey: 'imdb', fields: ['title', 'year'] }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { Step( function() { var i, group = this.group(); for (i = 0; i < data.length; i++) { bank.del('film', data[i].imdb, group()); } }, function(err) { if (err) throw err; bank.disconnect(this); }, this.callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can add some films': { topic: function(bank) { var cb = this.callback; Step( function() { var i = 0, group = this.group(); for ( i = 0; i < data.length; i++) { bank.save('film', data[i].imdb, data[i], group()); } }, cb ); }, 'without an error': function(err, films) { var i; assert.ifError(err); assert.isArray(films); assert.lengthOf(films, data.length); for (i = 0; i < films.length; i++) { assert.isObject(films[i]); assert.equal(films[i].imdb, data[i].imdb); } }, 'and we can scan a type with some data': { topic: function(films, bank) { var cb = this.callback, results = [], onResult = function(result) { results.push(result); }; bank.scan('film', onResult, function(err) { cb(err, results); }); }, 'without an error': function(err, results) { assert.ifError(err); }, 'with the correct data': function(err, results) { assert.ifError(err); assert.isArray(results); assert.lengthOf(results, data.length); } }, 'and we can scan a type previously unseen': { topic: function(films, bank) { var cb = this.callback, results = [], onResult = function(result) { results.push(result); }; bank.scan('bird', onResult, function(err) { cb(err, results); }); }, 'without an error': function(err, results) { assert.ifError(err); }, 'with the correct data': function(err, results) { assert.ifError(err); assert.isArray(results); assert.lengthOf(results, 0); } } } } }; return context; }; module.exports = scanContext; databank-0.19.1/lib/test/search.js000066400000000000000000000206101232327675000167050ustar00rootroot00000000000000// test/search.js // // Testing search() method // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../databank'), Databank = databank.Databank; var data = [ { brainz: 'c5b6f035-2965-4b14-8ab8-739c8ccef8a1', title: 'Free Ride', album: 'They Only Come Out at Night', artist: 'The Edgar Winter Group' }, { brainz: 'c9960e00-5ff6-47f4-ba97-32ebcad935cb', title: 'Goodbye Stranger', album: 'Breakfast in America', artist: 'Supertramp' }, { brainz: '0881386b-0275-4273-849d-4cf02b306a24', title: 'Dreamer', album: 'Crime of the Century', artist: 'Supertramp' }, { brainz: 'a1369840-e111-4ea7-9f2c-08e217c4645b', title: 'Take the Long Way Home', album: 'Breakfast in America', artist: 'Supertramp' }, { brainz: 'c45a3943-bdca-4753-b59a-5e10749c407e', title: 'Werewolves of London', album: 'Excitable Boy', artist: 'Warren Zevon' } ]; var searchContext = function(driver, params) { var context = {}; context["When we create a " + driver + " databank"] = { topic: function() { if (!params.hasOwnProperty('schema')) { params.schema = {}; } params.schema.song = { pkey: 'brainz', fields: ['artist', 'album', 'title'], indices: ['artist', 'album'] }; return Databank.get(driver, params); }, 'We can connect to it': { topic: function(bank) { bank.connect(params, this.callback); }, teardown: function(bank) { Step( function() { var i, group = this.group(); for (i = 0; i < data.length; i++) { bank.del('song', data[i].brainz, group()); } }, function(err) { if (err) throw err; bank.disconnect(this); }, this.callback ); }, 'without an error': function(err) { assert.ifError(err); }, 'and we can add some songs': { topic: function(bank) { var cb = this.callback; Step( function() { var i = 0, group = this.group(); for ( i = 0; i < data.length; i++) { bank.save('song', data[i].brainz, data[i], group()); } }, cb ); }, 'without an error': function(err, songs) { var i; assert.ifError(err); assert.isArray(songs); assert.lengthOf(songs, data.length); for (i = 0; i < songs.length; i++) { assert.isObject(songs[i]); assert.equal(songs[i].brainz, data[i].brainz); } }, 'and we can search by an indexed value': { topic: function(songs, bank) { var cb = this.callback, results = [], onResult = function(result) { results.push(result); }; bank.search('song', {'artist': 'Supertramp'}, onResult, function(err) { cb(err, results); }); }, 'without an error': function(err, results) { assert.ifError(err); }, 'with the correct data': function(err, results) { assert.ifError(err); assert.isArray(results); assert.lengthOf(results, 3); } }, 'and we can search by two indexed values': { topic: function(songs, bank) { var cb = this.callback, results = [], onResult = function(result) { results.push(result); }; bank.search('song', {'artist': 'Supertramp', 'album': 'Breakfast in America'}, onResult, function(err) { cb(err, results); }); }, 'without an error': function(err, results) { assert.ifError(err); }, 'with the correct data': function(err, results) { assert.ifError(err); assert.isArray(results); assert.lengthOf(results, 2); } }, 'and we can search by an non-indexed value': { topic: function(songs, bank) { var cb = this.callback, results = [], onResult = function(result) { results.push(result); }; bank.search('song', {'title': 'Werewolves of London'}, onResult, function(err) { cb(err, results); }); }, 'without an error': function(err, results) { assert.ifError(err); }, 'with the right data': function(err, results) { assert.ifError(err); assert.isArray(results); assert.lengthOf(results, 1); assert.equal(results[0].title, 'Werewolves of London'); } }, 'and we can search by mixed indexed and unindexed': { topic: function(songs, bank) { var cb = this.callback, results = [], onResult = function(result) { results.push(result); }; bank.search('song', {'title': 'Free Ride', 'artist': 'The Edgar Winter Group'}, onResult, function(err) { cb(err, results); }); }, 'without an error': function(err, results) { assert.ifError(err); }, 'with the right data': function(err, results) { assert.ifError(err); assert.isArray(results); assert.lengthOf(results, 1); assert.equal(results[0].title, 'Free Ride'); } }, 'and we can search with no expected results': { topic: function(songs, bank) { var cb = this.callback, results = [], onResult = function(result) { results.push(result); }; bank.search('song', {'artist': 'Batman'}, onResult, function(err) { cb(err, results); }); }, 'without an error': function(err, results) { assert.ifError(err); }, 'with an empty result set': function(err, results) { assert.ifError(err); assert.isArray(results); assert.lengthOf(results, 0); } } } } }; return context; }; module.exports = searchContext; databank-0.19.1/package.json000066400000000000000000000011051232327675000156410ustar00rootroot00000000000000{ "name": "databank", "version": "0.19.1", "homepage": "http://github.com/e14n/databank", "engines": { "node": ">=0.6.x" }, "author": "Evan Prodromou ", "scripts": {"test": "vows test/*-test.js"}, "main": "./lib/index.js", "directories": {"lib": "./lib/"}, "description": "Abstraction layer for JSON storage", "dependencies": { "vows": "0.7.x", "step": "0.0.x", "set-immediate": "~0.1.1" }, "repository": { "type": "git", "url": "http://github.com/e14n/databank.git" } } databank-0.19.1/test/000077500000000000000000000000001232327675000143355ustar00rootroot00000000000000databank-0.19.1/test/caching-driver-del-uncached-test.js000066400000000000000000000054021232327675000230500ustar00rootroot00000000000000// caching-driver-del-uncached-test.js // // Testing the caching driver // // Copyright 2014, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), databank = require('../lib/index'), Databank = databank.Databank; var suite = vows.describe("Caching driver with missing items").addBatch({ 'When we connect to the databank': { 'topic': function() { var params = { 'cache': {driver: 'memory', params: {}}, 'source': {driver: 'memory', params: {}}, 'schema': { 'bike': { 'pkey': 'id' } } }, db = Databank.get("caching", params), callback = this.callback; db.connect({}, function(err) { callback(err, db); }); }, 'it works': function(err, db) { assert.ifError(err); assert.isObject(db); }, 'and we create an object': { 'topic': function(db) { db.create('bike', 307, {'id': 307, 'name': 'Simone'}, this.callback); }, 'it works': function(err, bike) { assert.ifError(err); assert.isObject(bike); assert.equal(bike.id, 307); assert.equal(bike.name, 'Simone'); }, 'and we simulate cache expiration by deleting the object from the cache databank': { 'topic': function(bike, db) { // Note: this is probably a bug in the caching databank interface! db.cache.del('bike', bike.id, this.callback); }, 'it works': function(err) { assert.ifError(err); }, 'and we delete the object from the databank': { 'topic': function(bike, db) { db.del('bike', bike.id, this.callback); }, 'it works': function(err) { assert.ifError(err); } } } } } }); suite['export'](module); databank-0.19.1/test/caching-driver-test.js000066400000000000000000000016671232327675000205470ustar00rootroot00000000000000// caching-driver-test.js // // Testing the caching driver // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), databank = require('../lib/index'); var params = { 'cache': {driver: 'memory', params: {}}, 'source': {driver: 'memory', params: {}} }; var suite = databank.DriverTest('caching', params); suite['export'](module); databank-0.19.1/test/databank-module-test.js000066400000000000000000000050761232327675000207100ustar00rootroot00000000000000// Testing basic crud functionality var assert = require('assert'), vows = require('vows'); vows.describe('databank module interface').addBatch({ 'When we require the databank module': { topic: function() { return require('../lib/databank'); }, 'we get a module back': function(databank) { assert.ok(databank); }, 'we can get the DatabankError class': { topic: function(databank) { return databank.DatabankError; }, 'which is a function': function (DatabankError) { assert.isFunction(DatabankError); } }, 'we can get the NotImplementedError class': { topic: function(databank) { return databank.NotImplementedError; }, 'which is a function': function (NotImplementedError) { assert.isFunction(NotImplementedError); } }, 'we can get the AlreadyExistsError class': { topic: function(databank) { return databank.AlreadyExistsError; }, 'which is a function': function (AlreadyExistsError) { assert.isFunction(AlreadyExistsError); } }, 'we can get the NoSuchThingError class': { topic: function(databank) { return databank.NoSuchThingError; }, 'which is a function': function (NoSuchThingError) { assert.isFunction(NoSuchThingError); } }, 'we can get the AlreadyConnectedError class': { topic: function(databank) { return databank.AlreadyConnectedError; }, 'which is a function': function (AlreadyConnectedError) { assert.isFunction(AlreadyConnectedError); } }, 'we can get the NotConnectedError class': { topic: function(databank) { return databank.NotConnectedError; }, 'which is a function': function (NotConnectedError) { assert.isFunction(NotConnectedError); } }, 'we can get the Databank class': { topic: function(databank) { return databank.Databank; }, 'which is a function': function(Databank) { assert.isFunction(Databank); }, 'which has a get() method': function(Databank) { assert.isFunction(Databank.get); } } } }).export(module); databank-0.19.1/test/databank-register-test.js000066400000000000000000000024201232327675000212350ustar00rootroot00000000000000// test/databank-register-test.js // // Test the registration interface for Databank // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), databank = require('../lib/databank'), Databank = databank.Databank, MemoryDatabank = require('../lib/drivers/memory'); vows.describe('databank register').addBatch({ 'When we register a databank module': { topic: function() { Databank.register('memoryalias', MemoryDatabank); return Databank.get('memoryalias', {}); }, "we get back a good value": function(db) { assert.isObject(db); assert.instanceOf(db, MemoryDatabank); } } }).export(module); databank-0.19.1/test/memory-data-test.js000066400000000000000000000111641232327675000200720ustar00rootroot00000000000000// Testing DatabankObject basic functionality var assert = require('assert'), vows = require('vows'), Step = require('step'), databank = require('../lib/databank'), Databank = databank.Databank, NoSuchThingError = databank.NoSuchThingError; var suite = vows.describe('memory data param'); suite.addBatch({ "When we connect with no data param": { topic: function() { var cb = this.callback, db = Databank.get("memory", {}); db.connect({}, function(err) { cb(err, db); }); }, "it works": function(err, db) { assert.ifError(err); }, "and we try to read some data": { topic: function(db) { var cb = this.callback; db.read("person", "evan", function(err, person) { if (err && err instanceof NoSuchThingError) { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, "it fails correctly": function(err) { assert.ifError(err); } } }, "When we connect with an empty data param": { topic: function() { var cb = this.callback, db = Databank.get("memory", {data: {}}); db.connect({}, function(err) { cb(err, db); }); }, "it works": function(err, db) { assert.ifError(err); }, "and we try to read some data": { topic: function(db) { var cb = this.callback; db.read("person", "evan", function(err, person) { if (err && err instanceof NoSuchThingError) { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, "it fails correctly": function(err) { assert.ifError(err); } } }, "When we connect with an non-empty data param": { topic: function() { var cb = this.callback, data = { person: { "evan": {name: "evan", age: 43}, "amita": {name: "amita", age: 6} } }, db = Databank.get("memory", {data: data}); db.connect({}, function(err) { cb(err, db); }); }, "it works": function(err, db) { assert.ifError(err); }, "and we try to read uninitialized data": { topic: function(db) { var cb = this.callback; db.read("person", "boris", function(err, person) { if (err && err instanceof NoSuchThingError) { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, "it fails correctly": function(err) { assert.ifError(err); } }, "and we try to read an uninitialized type": { topic: function(db) { var cb = this.callback; db.read("house", "4690 rue Pontiac", function(err, person) { if (err && err instanceof NoSuchThingError) { cb(null); } else if (err) { cb(err); } else { cb(new Error("Unexpected success")); } }); }, "it fails correctly": function(err) { assert.ifError(err); } }, "and we try to read initialized data": { topic: function(db) { db.read("person", "evan", this.callback); }, "it works": function(err, person) { assert.ifError(err); }, "data is correct": function(err, person) { assert.ifError(err); assert.isObject(person); assert.include(person, "name"); assert.equal(person.name, "evan"); assert.include(person, "age"); assert.equal(person.age, 43); } } } }); suite.export(module); databank-0.19.1/test/memory-driver-test.js000066400000000000000000000015051232327675000204520ustar00rootroot00000000000000// memory-driver-test.js // // Testing the memory driver // // Copyright 2012, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), databank = require('../lib/index'); var suite = databank.DriverTest('memory', {}); suite['export'](module); databank-0.19.1/test/partitioning-driver-test.js000066400000000000000000000026751232327675000216620ustar00rootroot00000000000000// partitioning-driver-test.js // // Testing the partitioning driver // // Copyright 2013, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), databank = require('../lib/index'), params = {}; // Kind of extreme: we make a different memory databank // for each type used in our tests. var types = [ "inbox", "shoes", "friends", "test", "cellphone", "locality", "probability", "computer-count", "painting", "album", "person", "flight", "stock", "auto", "planet", "appliance", "state", "website", "card", "species", "task", "grape", "film", "song", "user", "bird", "tree" ]; for (var i = 0; i < types.length; i++) { params[types[i]] = {driver: 'memory', params: {}}; }; var suite = databank.DriverTest('partitioning', params); suite['export'](module); databank-0.19.1/test/partitioning-driver-wildcard-test.js000066400000000000000000000023021232327675000234340ustar00rootroot00000000000000// partitioning-driver-wildcard-test.js // // Testing the partitioning driver with a wildcard databank // // Copyright 2013, E14N https://e14n.com/ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var assert = require('assert'), vows = require('vows'), databank = require('../lib/index'), params = {}; // Kind of extreme: we make a different memory databank // for each type used in our tests. var types = [ "grape", "film", "song", "bird" ]; for (var i = 0; i < types.length; i++) { params[types[i]] = {driver: 'memory', params: {}}; }; params['*'] = {driver: 'memory', params: {}}; var suite = databank.DriverTest('partitioning', params); suite['export'](module);