Skip to content
This repository
Browse code

Merge branch 'master' of github.com:mde/model

  • Loading branch information...
commit 5734ff7abd71f30665d5fd8eb0072fbce60b4dcf 2 parents 8ee341a + 8d60236
Larz Conwell authored September 05, 2012
333  lib/adapters/mongo/index.js
... ...
@@ -0,0 +1,333 @@
  1
+var file = require('utilities').file
  2
+  , driver = file.requireLocal('mongodb-wrapper')
  3
+  , utils = require('utilities')
  4
+  , operation = require('../../query/operation')
  5
+  , comparison = require('../../query/comparison')
  6
+  , Query = require('../../query/query').Query
  7
+  , datatypes = require('../../datatypes')
  8
+  , request = utils.request
  9
+  , BaseAdapter = require('../base_adapter').BaseAdapter
  10
+  , _baseConfig
  11
+  , _comparisonTypeMap
  12
+  , _collectionizeModelName;
  13
+
  14
+_baseConfig = {
  15
+  username: null
  16
+, dbname: null
  17
+, prefix: null
  18
+, password: null
  19
+, host: 'localhost'
  20
+, port: 27017
  21
+};
  22
+
  23
+_comparisonTypeMap = {
  24
+  'NotEqualTo': '$ne'
  25
+, 'In': '$in'
  26
+, 'GreaterThan': '$gt'
  27
+, 'LessThan': '$lt'
  28
+, 'GreaterThanOrEqual': '$gte'
  29
+, 'LessThanOrEqual': '$lte'
  30
+};
  31
+
  32
+_collectionizeModelName = function (name) {
  33
+  var collectionName = utils.inflection.pluralize(name);
  34
+  collectionName = utils.string.snakeize(collectionName);
  35
+  return collectionName;
  36
+};
  37
+
  38
+var Adapter = function (options) {
  39
+  var opts = options || {}
  40
+    , config;
  41
+
  42
+  this.name = 'mongo';
  43
+  this.config = _baseConfig;
  44
+  this.client = null;
  45
+
  46
+  utils.mixin(this.config, opts);
  47
+
  48
+  this.init.apply(this, arguments);
  49
+};
  50
+
  51
+Adapter.prototype = new BaseAdapter();
  52
+Adapter.prototype.constructor = Adapter;
  53
+
  54
+utils.mixin(Adapter.prototype, new (function () {
  55
+
  56
+  this._serializeSortOrder = function (sort) {
  57
+    var ret = {};
  58
+    if (sort) {
  59
+      for (var p in sort) {
  60
+        ret[p] = (sort[p] == 'asc') ? 1 : -1;
  61
+      }
  62
+    }
  63
+    return ret;
  64
+  };
  65
+
  66
+  this._serializeConditions = function (conditions) {
  67
+    return this._serializeOperation(conditions);
  68
+  };
  69
+
  70
+  this._serializeOperation = function (op) {
  71
+    var self = this
  72
+      , ops = []
  73
+      , ret = {};
  74
+    if (!op.isEmpty()) {
  75
+      if (op.type == 'not') {
  76
+        ret = {'$nor': [self._serializeOperation(op.operand())]};
  77
+      }
  78
+      else {
  79
+        // 'and' or 'or', ignore 'null' for now
  80
+        ret['$' + op.type] = ops;
  81
+        op.forEach(function (o) {
  82
+          if (o instanceof operation.OperationBase) {
  83
+            ops.push(self._serializeOperation(o));
  84
+          }
  85
+          else {
  86
+            ops.push(self._serializeComparison(o));
  87
+          }
  88
+        });
  89
+      }
  90
+    }
  91
+    return ret;
  92
+  };
  93
+
  94
+  this._serializeComparison = function (comp) {
  95
+    var ret = {}
  96
+      , nocase = comp.opts.nocase
  97
+      , complex
  98
+      , re
  99
+      , val = comp.value;
  100
+
  101
+    //if (comp.datatype == 'date' || comp.datetime == 'datetime') {
  102
+    //  val = JSON.stringify(val).replace(/"/g, '');
  103
+    //}
  104
+
  105
+    switch (comp.type) {
  106
+      case 'EqualTo':
  107
+        // Case-insensitive equality via regex
  108
+        if (nocase) {
  109
+          val = val.toLowerCase();
  110
+          re = new RegExp('^' + val + '$', 'i');
  111
+          ret[comp.field] = re;
  112
+        }
  113
+        else {
  114
+          ret[comp.field] = val;
  115
+        }
  116
+        break;
  117
+      // Convert to regex
  118
+      case 'Like':
  119
+        if (nocase) {
  120
+          val = val.toLowerCase();
  121
+          re = new RegExp('^' + val, 'i');
  122
+        }
  123
+        else {
  124
+          re = new RegExp('^' + val);
  125
+        }
  126
+        ret[comp.field] = re;
  127
+        break;
  128
+      default:
  129
+        complex = {};
  130
+        complex[_comparisonTypeMap[comp.type]] = val;
  131
+        ret[comp.field] = complex;
  132
+    }
  133
+    return ret;
  134
+  };
  135
+
  136
+  this.init = function () {
  137
+    var config = this.config
  138
+      , args = [];
  139
+    ['host', 'port', 'dbname', 'prefix', 'username',
  140
+        'password'].forEach(function (c) {
  141
+      args.push(config[c]);
  142
+    });
  143
+    this.client = driver.db.apply(driver, args);
  144
+  };
  145
+
  146
+  this.load = function (query, callback) {
  147
+    var collectionName = _collectionizeModelName(query.model.modelName)
  148
+      , collection = this.client.collection(collectionName)
  149
+      , id = query.byId
  150
+      , conditions
  151
+      , sort;
  152
+
  153
+    // Single instance-lookup by id
  154
+    if (id) {
  155
+      collection.findOne({id: id}, function (err, data) {
  156
+        var inst
  157
+          , res = [];
  158
+        if (err) {
  159
+          // Not found?
  160
+          //if (err.statusCode == 404) {
  161
+          //  callback(null, null);
  162
+          //}
  163
+          //else {
  164
+            callback(err, null);
  165
+          //}
  166
+        }
  167
+        else {
  168
+          if (data) {
  169
+            inst = query.model.create(data);
  170
+            inst.id = id;
  171
+            inst._id = data._id;
  172
+            inst._saved = true;
  173
+            res.push(inst);
  174
+          }
  175
+          // If explicitly limited to one, just return the single instance
  176
+          // This is also used by the `first` method
  177
+          if (query.opts.limit == 1) {
  178
+            res = res[0];
  179
+          }
  180
+          callback(null, res);
  181
+        }
  182
+      });
  183
+    }
  184
+    // Collection
  185
+    else {
  186
+      conditions = this._serializeConditions(query.conditions);
  187
+      sort = this._serializeSortOrder(query.opts.sort);
  188
+
  189
+      //var util = require('util');
  190
+      //console.log(util.inspect(conditions, false, null));
  191
+
  192
+      collection.find(conditions, {})
  193
+          .sort(sort)
  194
+          .toArray(function (err, data) {
  195
+        var rows
  196
+          , res = [];
  197
+        if (err) {
  198
+          callback(err, null);
  199
+        }
  200
+        else {
  201
+          rows = data;
  202
+          rows.forEach(function (row) {
  203
+            var inst = query.model.create(row);
  204
+            inst.id = row.id;
  205
+            inst._id = row._id;
  206
+            inst._saved = true;
  207
+            res.push(inst);
  208
+          });
  209
+          // If explicitly limited to one, just return the single instance
  210
+          // This is also used by the `first` method
  211
+          if (query.opts.limit == 1) {
  212
+            res = res[0];
  213
+          }
  214
+          callback(null, res);
  215
+        }
  216
+      });
  217
+    }
  218
+  };
  219
+
  220
+
  221
+  this.update = function (data, query, opts, callback) {
  222
+    var collectionName = _collectionizeModelName(query.model.modelName)
  223
+      , collection = this.client.collection(collectionName)
  224
+      , id = query.byId
  225
+      , item = data;
  226
+    // Single instance-lookup by id
  227
+    if (id) {
  228
+      // Bail out if instance isn't valid
  229
+      if (!item.isValid()) {
  230
+        return callback(data.errors, null);
  231
+      }
  232
+
  233
+      item = item.toData({whitelist: ['_id', 'id', 'createdAt']});
  234
+
  235
+      collection.update({id: id}, item, function (err, data) {
  236
+        if (err) {
  237
+          callback(err, null);
  238
+        }
  239
+        else {
  240
+          // FIXME: What is the right data to return here? Right now this
  241
+          // is basically overwriting a doc, but we might be supporting
  242
+          // bulk-updates at some point
  243
+          callback(null, true);
  244
+        }
  245
+      });
  246
+    }
  247
+    // Bulk update?
  248
+    else {
  249
+      callback(new Error('Bulk update is not supported'), null);
  250
+    }
  251
+  };
  252
+
  253
+  this.remove = function (query, opts, callback) {
  254
+    var collectionName = _collectionizeModelName(query.model.modelName)
  255
+      , collection = this.client.collection(collectionName)
  256
+      , id = query.byId
  257
+      , conditions;
  258
+
  259
+    // Single instance-lookup by id
  260
+    if (id) {
  261
+      conditions = {id: id};
  262
+    }
  263
+    // Collection
  264
+    else {
  265
+      conditions = this._serializeConditions(query.conditions);
  266
+    }
  267
+    collection.remove(conditions, function (err, data) {
  268
+      var inst
  269
+        , res = [];
  270
+      if (err) {
  271
+        callback(err, null);
  272
+      }
  273
+      else {
  274
+        callback(null, true);
  275
+      }
  276
+    });
  277
+  };
  278
+
  279
+  this.insert = function (data, opts, callback) {
  280
+    var self = this
  281
+      , items = Array.isArray(data) ? data.slice() : [data]
  282
+      , collectionName = _collectionizeModelName(items[0].type)
  283
+      , collection = this.client.collection(collectionName)
  284
+      , ret = []
  285
+      , insert;
  286
+
  287
+    insert = function () {
  288
+      var item;
  289
+      if ((item = items.shift())) {
  290
+        var id = utils.string.uuid()
  291
+
  292
+        item.id = id;
  293
+        item = item.toData({whitelist: ['id', 'createdAt']});
  294
+
  295
+        collection.insert(item, function (err, res) {
  296
+          if (err) {
  297
+            callback(err, null);
  298
+          }
  299
+          else {
  300
+            item.id = id;
  301
+            item._id = res._id;
  302
+            item._saved = true;
  303
+            ret.push(data);
  304
+            insert();
  305
+          }
  306
+        });
  307
+      }
  308
+      else {
  309
+        callback(null, ret);
  310
+      }
  311
+    };
  312
+    insert();
  313
+  };
  314
+
  315
+  this.createTable = function (names, callback) {
  316
+    var self = this
  317
+      , collections = Array.isArray(names) ? names.slice() : [names]
  318
+      , create = function () {
  319
+          var c;
  320
+          if ((c = collections.shift())) {
  321
+            self.client.createCollection(c, {}, create);
  322
+          }
  323
+          else {
  324
+            callback();
  325
+          }
  326
+        };
  327
+    create();
  328
+  };
  329
+
  330
+})());
  331
+
  332
+module.exports.Adapter = Adapter;
  333
+
6  lib/adapters/riak/index.js
@@ -165,7 +165,7 @@ utils.mixin(Adapter.prototype, new (function () {
165 165
     // Use bracket-notation, in case field-name has special chars
166 166
     // or is a reserved word
167 167
     var name = 'data[\'' + comp.field + '\']';
168  
-    if (comp.opts.lowercase) {
  168
+    if (comp.opts.nocase) {
169 169
       name += '.toLowerCase()';
170 170
     }
171 171
     return name;
@@ -354,13 +354,11 @@ utils.mixin(Adapter.prototype, new (function () {
354 354
 
355 355
   this.insert = function (data, opts, callback) {
356 356
     var self = this
357  
-      , items = Array.isArray(data) ? data : [data]
  357
+      , items = Array.isArray(data) ? data.slice() : [data]
358 358
       , bucket = _bucketizeModelName(items[0].type)
359 359
       , ret = []
360 360
       , insert;
361 361
 
362  
-    items = items.slice();
363  
-
364 362
     insert = function () {
365 363
       var item;
366 364
       if ((item = items.shift())) {
18  lib/adapters/sql/base.js
@@ -64,18 +64,18 @@ utils.mixin(Adapter.prototype, new (function () {
64 64
       return '';
65 65
     }
66 66
     else {
67  
-      op.forEach(function (o) {
68  
-        if (o instanceof operation.OperationBase) {
69  
-          ops.push(self._serializeOperation(o));
70  
-        }
71  
-        else {
72  
-          ops.push(self._serializeComparison(o));
73  
-        }
74  
-      });
75 67
       if (op.type == 'not') {
76 68
         return 'NOT (' + self._serializeOperation(op.operand()) + ')';
77 69
       }
78 70
       else {
  71
+        op.forEach(function (o) {
  72
+          if (o instanceof operation.OperationBase) {
  73
+            ops.push(self._serializeOperation(o));
  74
+          }
  75
+          else {
  76
+            ops.push(self._serializeComparison(o));
  77
+          }
  78
+        });
79 79
         return '(' + ops.join(' ' + op.type.toUpperCase() + ' ') + ')';
80 80
       }
81 81
     }
@@ -89,7 +89,7 @@ utils.mixin(Adapter.prototype, new (function () {
89 89
 
90 90
   this._serializeComparisonFieldName = function (comp) {
91 91
     var name = this._columnizePropertyName(comp.field);
92  
-    if (comp.opts.lowercase) {
  92
+    if (comp.opts.nocase) {
93 93
       name = 'LOWER(' + name + ')';
94 94
     }
95 95
     return name;
4  lib/adapters/sql/postgres.js
... ...
@@ -1,5 +1,5 @@
1  
-
2  
-var pg = require('pg')
  1
+var file = require('utilities').file
  2
+  , pg = file.requireLocal('pg')
3 3
   , generator = require('../../../lib/generators/sql')
4 4
   , utils = require('utilities')
5 5
   , model = require('../../../lib')
7  lib/index.js
@@ -507,8 +507,11 @@ utils.mixin(model, new (function () {
507 507
     return item;
508 508
   };
509 509
 
510  
-  this.updateItem = function (item, params) {
511  
-    item = this.validateAndUpdateFromParams(item, params);
  510
+  this.updateItem = function (item, params, opts) {
  511
+    var data = {};
  512
+    utils.mixin(data, item);
  513
+    utils.mixin(data, params);
  514
+    this.validateAndUpdateFromParams(item, data, opts);
512 515
 
513 516
     // After-update hook
514 517
     if (typeof item.afterUpdate === 'function') {
30  lib/query/query.js
@@ -12,20 +12,20 @@ Query = function (model, conditions, options) {
12 12
   this.initialize.apply(this, arguments);
13 13
 };
14 14
 
15  
-Query.prototype = new (function () {
  15
+Query.comparisonTypes = {
  16
+  'eql': 'EqualTo'
  17
+, 'ne': 'NotEqualTo'
  18
+, 'in': 'In'
  19
+, 'like': 'Like'
  20
+, 'gt': 'GreaterThan'
  21
+, 'lt': 'LessThan'
  22
+, 'gte': 'GreaterThanOrEqual'
  23
+, 'lte': 'LessThanOrEqual'
  24
+};
16 25
 
17  
-  var _comparisonCtorNames = {
18  
-        'eql': 'EqualTo'
19  
-      , 'ne': 'NotEqualTo'
20  
-      , 'in': 'In'
21  
-      , 'like': 'Like'
22  
-      , 'gt': 'GreaterThan'
23  
-      , 'lt': 'LessThan'
24  
-      , 'gte': 'GreaterThanOrEqual'
25  
-      , 'lte': 'LessThanOrEqual'
26  
-      }
  26
+Query.prototype = new (function () {
27 27
 
28  
-    , _operationTypes = {
  28
+  var _operationTypes = {
29 29
         'and': true
30 30
       , 'or': true
31 31
       , 'not': true
@@ -137,15 +137,15 @@ Query.prototype = new (function () {
137 137
         // TODO: Validate the value for both the particular field
138 138
         // (e.g., must be a numeric) and the type of comparison
139 139
         // (e.g., 'IN' must be a collection, etc)
140  
-        return comparison.create(_comparisonCtorNames[type], key, val,
  140
+        return comparison.create(Query.comparisonTypes[type], key, val,
141 141
             datatype, opts);
142 142
       }
143 143
 
144 144
     , _createComparisonOpts = function (key) {
145 145
         var opts = this.opts
146  
-          , lowercase = opts.lowercase
  146
+          , nocase = opts.nocase
147 147
           , ret = {};
148  
-        ['lowercase'].forEach(function (k) {
  148
+        ['nocase'].forEach(function (k) {
149 149
           var opt = opts[k]
150 150
             , included;
151 151
           if (opt) {
59  test/adapters/mongo/index.js
... ...
@@ -0,0 +1,59 @@
  1
+var utils = require('utilities')
  2
+  , model = require('../../../lib')
  3
+  , Adapter = require('../../../lib/adapters/mongo').Adapter
  4
+  , Query = require('../../../lib/query/query').Query
  5
+  , generator = require('../../../lib/generators/sql')
  6
+  , adapter
  7
+  , assert = require('assert')
  8
+  , currentId
  9
+  , tests
  10
+  , testItems = []
  11
+  , Zooby = require('../../fixtures/zooby').Zooby
  12
+  , User = require('../../fixtures/user').User
  13
+  , Profile = require('../../fixtures/profile').Profile
  14
+  , Account = require('../../fixtures/account').Account
  15
+  , shared = require('../shared');
  16
+
  17
+tests = {
  18
+  'before': function (next) {
  19
+    adapter = new Adapter({
  20
+      dbname: 'model_test'
  21
+    });
  22
+
  23
+    model.adapters = {
  24
+      'Zooby': adapter
  25
+    , 'User': adapter
  26
+    , 'Profile': adapter
  27
+    , 'Account': adapter
  28
+    };
  29
+
  30
+    Zooby.remove({}, function (err, data) {
  31
+      if (err) {
  32
+        throw err;
  33
+      }
  34
+      next();
  35
+    });
  36
+  }
  37
+
  38
+, 'after': function (next) {
  39
+    Zooby.remove({}, function (err, data) {
  40
+      if (err) {
  41
+        throw err;
  42
+      }
  43
+      next();
  44
+    });
  45
+  }
  46
+
  47
+, 'test create adapter': function () {
  48
+    assert.ok(adapter instanceof Adapter);
  49
+  }
  50
+
  51
+
  52
+};
  53
+
  54
+for (var p in shared) {
  55
+  tests[p + ' (Mongo)'] = shared[p];
  56
+}
  57
+
  58
+module.exports = tests;
  59
+
281  test/adapters/riak/index.js
@@ -11,7 +11,8 @@ var utils = require('utilities')
11 11
   , Zooby = require('../../fixtures/zooby').Zooby
12 12
   , User = require('../../fixtures/user').User
13 13
   , Profile = require('../../fixtures/profile').Profile
14  
-  , Account = require('../../fixtures/account').Account;
  14
+  , Account = require('../../fixtures/account').Account
  15
+  , shared = require('../shared');
15 16
 
16 17
 tests = {
17 18
   'before': function () {
@@ -29,280 +30,10 @@ tests = {
29 30
     assert.ok(adapter instanceof Adapter);
30 31
   }
31 32
 
32  
-, 'test save new, string UUID id': function (next) {
33  
-    var z = Zooby.create({foo: 'FOO'});
34  
-    z.save(function (err, data) {
35  
-      if (err) {
36  
-        throw err;
37  
-      }
38  
-      currentId = z.id;
39  
-      next();
40  
-    });
41  
-  }
42  
-
43  
-, 'test first via string id': function (next) {
44  
-    Zooby.first(currentId, {}, function (err, data) {
45  
-      if (err) {
46  
-        throw err;
47  
-      }
48  
-      assert.equal(currentId, data.id);
49  
-      next();
50  
-    });
51  
-  }
52  
-
53  
-, 'test save existing': function (next) {
54  
-    Zooby.first(currentId, {}, function (err, data) {
55  
-      if (err) {
56  
-        throw err;
57  
-      }
58  
-      var inst = data;
59  
-      data.updateProperties({
60  
-        foo: 'ZZZ'
61  
-      });
62  
-      inst.save(function (err, data) {
63  
-        if (err) {
64  
-          throw err;
65  
-        }
66  
-        Zooby.first(currentId, {}, function (err, data) {
67  
-          if (err) {
68  
-            throw err;
69  
-          }
70  
-          assert.equal('ZZZ', data.foo);
71  
-          next();
72  
-        });
73  
-      });
74  
-    });
75  
-  }
76  
-
77  
-, 'test all, by id': function (next) {
78  
-    Zooby.all({id: currentId}, {}, function (err, data) {
79  
-      if (err) {
80  
-        throw err;
81  
-      }
82  
-      assert.equal(currentId, data[0].id);
83  
-      next();
84  
-    });
85  
-  }
86  
-
87  
-, 'test all, by map-reduce, equality': function (next) {
88  
-    Zooby.all({foo: 'ZZZ'}, {}, function (err, data) {
89  
-      if (err) {
90  
-        throw err;
91  
-      }
92  
-      assert.equal(data[0].id, currentId);
93  
-      next();
94  
-    });
95  
-  }
96  
-
97  
-, 'test all, by map-reduce, like': function (next) {
98  
-    Zooby.all({foo: {like: 'Z'}}, {}, function (err, data) {
99  
-      if (err) {
100  
-        throw err;
101  
-      }
102  
-      assert.equal(data[0].id, currentId);
103  
-      next();
104  
-    });
105  
-  }
106  
-
107  
-, 'test all, by map-reduce, like lowercased': function (next) {
108  
-    Zooby.all({foo: {like: 'z'}}, {lowercase: ['foo']}, function (err, data) {
109  
-      if (err) {
110  
-        throw err;
111  
-      }
112  
-      assert.equal(data[0].id, currentId);
113  
-      next();
114  
-    });
115  
-  }
116  
-
117  
-, 'test all, by map-reduce, equality and like': function (next) {
118  
-    Zooby.all({createdAt: {ne: null}, foo: {like: 'Z'}}, {}, function (err, data) {
119  
-      if (err) {
120  
-        throw err;
121  
-      }
122  
-      assert.equal(data[0].id, currentId);
123  
-      next();
124  
-    });
125  
-  }
126  
-
127  
-, 'test save collection': function (next) {
128  
-    var dt = new Date();
129  
-    testItems.push(Zooby.create({
130  
-      foo: 'FOO'
131  
-    , zong: utils.date.add(dt, 'day', -1)
132  
-    }));
133  
-    testItems.push(Zooby.create({
134  
-      foo: 'BAR'
135  
-    , zong: utils.date.add(dt, 'day', -2)
136  
-    }));
137  
-    testItems.push(Zooby.create({
138  
-      foo: 'BAZ'
139  
-    , zong: utils.date.add(dt, 'day', -3)
140  
-    }));
141  
-    Zooby.save(testItems, function (err, data) {
142  
-      if (err) {
143  
-        throw err;
144  
-      }
145  
-      next();
146  
-    });
147  
-  }
148  
-
149  
-, 'test all collection, by map-reduce, equality': function (next) {
150  
-    Zooby.all({foo: 'FOO'}, {}, function (err, data) {
151  
-      if (err) {
152  
-        throw err;
153  
-      }
154  
-      assert.equal(data.length, 1);
155  
-      assert.equal(testItems[0].foo, data[0].foo);
156  
-      next();
157  
-    });
158  
-  }
159  
-
160  
-, 'test all, by string LIKE case-sensitive': function (next) {
161  
-    Zooby.all({foo: {'like': 'B'}}, {}, function (err, data) {
162  
-      if (err) {
163  
-        throw err;
164  
-      }
165  
-      assert.equal(data.length, 2);
166  
-      next();
167  
-    });
168  
-  }
169  
-
170  
-, 'test all, by string LIKE case-insensitive bool': function (next) {
171  
-    Zooby.all({foo: {'like': 'b'}}, {lowercase: true}, function (err, data) {
172  
-      if (err) {
173  
-        throw err;
174  
-      }
175  
-      assert.equal(data.length, 2);
176  
-      next();
177  
-    });
178  
-  }
179  
-
180  
-, 'test all, by LIKE case-insensitive array': function (next) {
181  
-    Zooby.all({foo: {'like': 'b'}}, {lowercase: ['foo']}, function (err, data) {
182  
-      if (err) {
183  
-        throw err;
184  
-      }
185  
-      assert.equal(data.length, 2);
186  
-      next();
187  
-    });
188  
-  }
189  
-
190  
-, 'test all, sort string column name': function (next) {
191  
-    Zooby.all({}, {sort: 'zong'}, function (err, data) {
192  
-      if (err) {
193  
-        throw err;
194  
-      }
195  
-      next();
196  
-    });
197  
-  }
198  
-
199  
-, 'test all, sort incorrect string column name': function () {
200  
-    assert.throws(function () {
201  
-      Zooby.all({}, {sort: 'zongX'}, function (err, data) {
202  
-      });
203  
-    }, Error);
204  
-  }
205  
-
206  
-, 'test all, sort array column names': function (next) {
207  
-    Zooby.all({}, {sort: ['foo', 'zong']}, function (err, data) {
208  
-      // Should be sorted BAR, BAZ, FOO
209  
-      assert.equal(data[0].id, testItems[1].id);
210  
-      if (err) {
211  
-        throw err;
212  
-      }
213  
-      next();
214  
-    });
215  
-  }
216  
-
217  
-, 'test all, sort object literal desc': function (next) {
218  
-    Zooby.all({}, {sort: {zong: 'desc'}}, function (err, data) {
219  
-      // Sort by datetime
220  
-      assert.equal(data[0].id, testItems[0].id);
221  
-      if (err) {
222  
-        throw err;
223  
-      }
224  
-      next();
225  
-    });
226  
-  }
227  
-
228  
-/*
229  
-, 'test all, sort object literal asc': function (next) {
230  
-    Zooby.all({}, {sort: {zong: 'asc'}}, function (err, data) {
231  
-      // Sort by datetime reversed
232  
-      assert.equal(data[0].id, testItems[2].id);
233  
-      if (err) {
234  
-        throw err;
235  
-      }
236  
-      next();
237  
-    });
238  
-  }
239  
-*/
240  
-
241  
-, 'test all, sort incorrect sort direction': function () {
242  
-    assert.throws(function () {
243  
-      Zooby.all({}, {sort: {foo: 'asc', bar: 'descX'}}, function (err, data) {
244  
-      });
245  
-    }, Error);
246  
-  }
247  
-
248  
-, 'test all, using or, simple equality': function (next) {
249  
-    Zooby.all({or: [{foo: 'BAR'}, {foo: 'BAZ'}]}, {}, function (err, data) {
250  
-      assert.equal(data.length, 2);
251  
-      if (err) {
252  
-        throw err;
253  
-      }
254  
-      next();
255  
-    });
256  
-  }
257  
-
258  
-, 'test all, using or, like comparison': function (next) {
259  
-    Zooby.all({or: [{foo: {'like': 'b'}}, {foo: 'foo'}]}, {lowercase: ['foo']},
260  
-        function (err, data) {
261  
-      assert.equal(data.length, 3);
262  
-      if (err) {
263  
-        throw err;
264  
-      }
265  
-      next();
266  
-    });
267  
-  }
268  
-
269  
-, 'test all, using or, like comparison with not': function (next) {
270  
-    Zooby.all({or: [{foo: {'like': 'b'}}, {foo: 'foo'}], not: {foo: 'baz'}},
271  
-        {lowercase: ['foo']}, function (err, data) {
272  
-      assert.equal(data.length, 2);
273  
-      if (err) {
274  
-        throw err;
275  
-      }
276  
-      next();
277  
-    });
278  
-  }
279  
-
280  
-, 'test all, using less-than createdAt': function (next) {
281  
-    Zooby.all({createdAt: {lt: new Date()}},
282  
-        {}, function (err, data) {
283  
-      assert.equal(data.length, 4);
284  
-      if (err) {
285  
-        throw err;
286  
-      }
287  
-      next();
288  
-    });
289  
-  }
290  
-
291  
-, 'test remove': function (next) {
292  
-    Zooby.remove(currentId, {}, function (err, data) {
293  
-      if (err) {
294  
-        throw err;
295  
-      }
296  
-      Zooby.first(currentId, {}, function (err, data) {
297  
-        if (err) {
298  
-          throw err;
299  
-        }
300  
-        assert.ok(!data);
301  
-        next();
302  
-      });
303  
-    });
304  
-  }
305  
-
306 33
 };
307 34
 
  35
+for (var p in shared) {
  36
+  tests[p + ' (Riak)'] = shared[p];
  37
+}
  38
+
308 39
 module.exports = tests;
254  test/adapters/shared.js
... ...
@@ -0,0 +1,254 @@
  1
+var utils = require('utilities')
  2
+  , assert = require('assert')
  3
+  , currentId
  4
+  , tests
  5
+  , testItems = []
  6
+  , Zooby = require('../fixtures/zooby').Zooby
  7
+  , User = require('../fixtures/user').User
  8
+  , Profile = require('../fixtures/profile').Profile
  9
+  , Account = require('../fixtures/account').Account;
  10
+
  11
+tests = {
  12
+
  13
+  'test save new, string UUID id': function (next) {
  14
+    var z = Zooby.create({
  15
+      foo: 'FOO'
  16
+    , zong: new Date()
  17
+    });
  18
+    z.save(function (err, data) {
  19
+      if (err) {
  20
+        throw err;
  21
+      }
  22
+      currentId = z.id;
  23
+      next();
  24
+    });
  25
+  }
  26
+
  27
+, 'test first via string id': function (next) {
  28
+    Zooby.first(currentId, {}, function (err, data) {
  29
+      if (err) {
  30
+        throw err;
  31
+      }
  32
+      assert.equal(data.id, currentId);
  33
+      next();
  34
+    });
  35
+  }
  36
+
  37
+// TODO: Load via array of ids
  38
+
  39
+, 'test first via object': function (next) {
  40
+    Zooby.first({id: currentId}, {}, function (err, data) {
  41
+      if (err) {
  42
+        throw err;
  43
+      }
  44
+      assert.equal(data.id, currentId);
  45
+      next();
  46
+    });
  47
+  }
  48
+
  49
+, 'test save existing': function (next) {
  50
+    Zooby.first(currentId, {}, function (err, data) {
  51
+      if (err) {
  52
+        throw err;
  53
+      }
  54
+      var inst = data;
  55
+      data.updateProperties({
  56
+        foo: 'ZZZ'
  57
+      });
  58
+      inst.save(function (err, data) {
  59
+        if (err) {
  60
+          throw err;
  61
+        }
  62
+        Zooby.first(currentId, {}, function (err, data) {
  63
+          if (err) {
  64
+            throw err;
  65
+          }
  66
+          assert.ok(data);
  67
+          assert.equal(data.foo, 'ZZZ');
  68
+          next();
  69
+        });
  70
+      });
  71
+    });
  72
+  }
  73
+
  74
+, 'test save collection': function (next) {
  75
+    var dt = new Date();
  76
+    testItems.push(Zooby.create({
  77
+      foo: 'FOO'
  78
+    , zong: utils.date.add(dt, 'day', -1)
  79
+    }));
  80
+    testItems.push(Zooby.create({
  81
+      foo: 'BAR'
  82
+    , zong: utils.date.add(dt, 'day', -2)
  83
+    }));
  84
+    testItems.push(Zooby.create({
  85
+      foo: 'BAZ'
  86
+    , zong: utils.date.add(dt, 'day', -3)
  87
+    }));
  88
+    Zooby.save(testItems, function (err, data) {
  89
+      if (err) {
  90
+        throw err;
  91
+      }
  92
+      next();
  93
+    });
  94
+  }
  95
+
  96
+, 'test all, by string equality': function (next) {
  97
+    Zooby.all({foo: 'FOO'}, {}, function (err, data) {
  98
+      if (err) {
  99
+        throw err;
  100
+      }
  101
+      assert.equal(data.length, 1);
  102
+      assert.equal(testItems[0].foo, data[0].foo);
  103
+      next();
  104
+    });
  105
+  }
  106
+
  107
+, 'test all, by string LIKE case-sensitive': function (next) {
  108
+    Zooby.all({foo: {'like': 'B'}}, {}, function (err, data) {
  109
+      if (err) {
  110
+        throw err;
  111
+      }
  112
+      assert.equal(data.length, 2);
  113
+      next();
  114
+    });
  115
+  }
  116
+
  117
+, 'test all, by string LIKE case-insensitive bool': function (next) {
  118
+    Zooby.all({foo: {'like': 'b'}}, {nocase: true}, function (err, data) {
  119
+      if (err) {
  120
+        throw err;
  121
+      }
  122
+      assert.equal(data.length, 2);
  123
+      next();
  124
+    });
  125
+  }
  126
+
  127
+, 'test all, by LIKE case-insensitive array': function (next) {
  128
+    Zooby.all({foo: {'like': 'b'}}, {nocase: ['foo']}, function (err, data) {
  129
+      if (err) {
  130
+        throw err;
  131
+      }
  132
+      assert.equal(data.length, 2);
  133
+      next();
  134
+    });
  135
+  }
  136
+
  137
+, 'test all, sort string column name': function (next) {
  138
+    Zooby.all({}, {sort: 'zong'}, function (err, data) {
  139
+      if (err) {
  140
+        throw err;
  141
+      }
  142
+      next();
  143
+    });
  144
+  }
  145
+
  146
+, 'test all, sort incorrect string column name': function () {
  147
+    assert.throws(function () {
  148
+      Zooby.all({}, {sort: 'zongX'}, function (err, data) {
  149
+      });
  150
+    }, Error);
  151
+  }
  152
+
  153
+, 'test all, sort array column names': function (next) {
  154
+    Zooby.all({}, {sort: ['foo', 'zong']}, function (err, data) {
  155
+      // Should be BAR, BAZ, FOO, ZZZ
  156
+      assert.equal(data[0].id, testItems[1].id);
  157
+      if (err) {
  158
+        throw err;
  159
+      }
  160
+      next();
  161
+    });
  162
+  }
  163
+
  164
+, 'test all, sort object literal desc': function (next) {
  165
+    Zooby.all({}, {sort: {zong: 'desc'}}, function (err, data) {
  166
+      // Should be sorted ZZZ, FOO, BAR, BAZ
  167
+      // Sort by datetime
  168
+      assert.equal(data[0].id, currentId);
  169
+      if (err) {
  170
+        throw err;
  171
+      }
  172
+      next();
  173
+    });
  174
+  }
  175
+
  176
+, 'test all, sort object literal asc': function (next) {
  177
+    Zooby.all({}, {sort: {zong: 'asc'}}, function (err, data) {
  178
+      // Sort by datetime reversed
  179
+      assert.equal(data[0].id, testItems[2].id);
  180
+      if (err) {
  181
+        throw err;
  182
+      }
  183
+      next();
  184
+    });
  185
+  }
  186
+
  187
+, 'test all, sort incorrect sort direction': function () {
  188
+    assert.throws(function () {
  189
+      Zooby.all({}, {sort: {foo: 'asc', bar: 'descX'}}, function (err, data) {
  190
+      });
  191
+    }, Error);
  192
+  }
  193
+
  194
+, 'test all, using or, simple equality': function (next) {
  195
+    Zooby.all({or: [{foo: 'BAR'}, {foo: 'BAZ'}]}, {}, function (err, data) {
  196
+      assert.equal(2, data.length);
  197
+      if (err) {
  198
+        throw err;
  199
+      }
  200
+      next();
  201
+    });
  202
+  }
  203
+
  204
+, 'test all, using or, like comparison': function (next) {
  205
+    Zooby.all({or: [{foo: {'like': 'b'}}, {foo: 'foo'}]}, {nocase: ['foo']},
  206
+        function (err, data) {
  207
+      assert.equal(3, data.length);
  208
+      if (err) {
  209
+        throw err;
  210
+      }
  211
+      next();
  212
+    });
  213
+  }
  214
+
  215
+, 'test all, using or, like comparison with not': function (next) {
  216
+    Zooby.all({or: [{foo: {'like': 'b'}}, {foo: 'foo'}], not: {foo: 'baz'}},
  217
+        {nocase: ['foo']}, function (err, data) {
  218
+      assert.equal(data.length, 2);
  219
+      if (err) {
  220
+        throw err;
  221
+      }
  222
+      next();
  223
+    });
  224
+  }
  225
+
  226
+, 'test all, using less-than createdAt': function (next) {
  227
+    Zooby.all({createdAt: {lt: new Date()}},
  228
+        {}, function (err, data) {
  229
+      assert.equal(data.length, 4);
  230
+      if (err) {
  231
+        throw err;
  232
+      }
  233
+      next();
  234
+    });
  235
+  }
  236
+
  237
+, 'test remove': function (next) {
  238
+    Zooby.remove(currentId, {}, function (err, data) {
  239
+      if (err) {
  240
+        throw err;
  241
+      }
  242
+      Zooby.first(currentId, {}, function (err, data) {
  243
+        if (err) {
  244
+          throw err;
  245
+        }
  246
+        assert.ok(!data);
  247
+        next();
  248
+      });
  249
+    });
  250
+  }
  251
+
  252
+};
  253
+
  254
+module.exports = tests;
229  test/adapters/sql/postgres.js
@@ -17,11 +17,11 @@ var utils = require('utilities')
17 17
   , assert = require('assert')
18 18
   , currentId
19 19
   , tests
20  
-  , testItems = []
21 20
   , Zooby = require('../../fixtures/zooby').Zooby
22 21
   , User = require('../../fixtures/user').User
23 22
   , Profile = require('../../fixtures/profile').Profile
24  
-  , Account = require('../../fixtures/account').Account;
  23
+  , Account = require('../../fixtures/account').Account
  24
+  , shared = require('../shared');
25 25
 
26 26
 tests = {
27 27
   'before': function (next) {
@@ -202,64 +202,7 @@ tests = {
202 202
     });
203 203
   }
204 204
 
205  
-, 'test save new, string UUID id': function (next) {
206  
-    var z = Zooby.create({foo: 'FOO'});
207  
-    z.save(function (err, data) {
208  
-      if (err) {
209  
-        throw err;
210  
-      }
211  
-      currentId = z.id;
212  
-      next();
213  
-    });
214  
-  }
215  
-
216  
-, 'test first via string id': function (next) {
217  
-    Zooby.first(currentId, {}, function (err, data) {
218  
-      if (err) {
219  
-        throw err;
220  
-      }
221  
-      assert.equal(data.id, currentId);
222  
-      next();
223  
-    });
224  
-  }
225  
-
226  
-// TODO: Load via array of ids
227  
-
228  
-, 'test first via object': function (next) {
229  
-    Zooby.first({id: currentId}, {}, function (err, data) {
230  
-      if (err) {
231  
-        throw err;
232  
-      }
233  
-      assert.equal(data.id, currentId);
234  
-      next();
235  
-    });
236  
-  }
237  
-
238  
-, 'test save existing': function (next) {
239  
-    Zooby.first(currentId, {}, function (err, data) {
240  
-      if (err) {
241  
-        throw err;
242  
-      }
243  
-      var inst = data;
244  
-      data.updateProperties({
245  
-        foo: 'BAR'
246  
-      });
247  
-      inst.save(function (err, data) {
248  
-        if (err) {
249  
-          throw err;
250  
-        }
251  
-        Zooby.first(currentId, {}, function (err, data) {
252  
-          if (err) {
253  
-            throw err;
254  
-          }
255  
-          assert.equal(data.foo, 'BAR');
256  
-          next();
257  
-        });
258  
-      });
259  
-    });
260  
-  }
261  
-
262  
-, 'test remove': function (next) {
  205
+, 'test remove, auto-increment id': function (next) {
263 206
     Zooby.remove(currentId, {}, function (err, data) {
264 207
       if (err) {
265 208
         throw err;
@@ -274,168 +217,10 @@ tests = {
274 217
     });
275 218
   }
276 219
 
277  
-, 'test save collection': function (next) {
278  
-    var dt = new Date();
279  
-    testItems.push(Zooby.create({
280  
-      foo: 'FOO'
281  
-    , zong: utils.date.add(dt, 'day', -1)
282  
-    }));
283  
-    testItems.push(Zooby.create({