Skip to content

Commit af92fd2

Browse files
author
Michael Williams
committed
[persistence] [jugglingdb] changed the structure of persistence by moving some code around in order to better facilitate new persistence resources (#66)
1 parent 3cfef81 commit af92fd2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+367
-343
lines changed

fs/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ fs.method('start', function (cb) {
99
cb(null, true);
1010
});
1111

12+
fs.method('enable', resource.use('jugglingdb').enable);
13+
1214
fs.dependencies = {
1315
"mkdirp": "*"
1416
};

jugglingdb/index.js

+353
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
var resource = require('resource'),
2+
jugglingdb = resource.define('jugglingdb');
3+
4+
jugglingdb.schema.description = "enables jugglingdb for resources";
5+
6+
jugglingdb.method('enable', enable);
7+
8+
//
9+
// Enables a resource to jugglingdb by
10+
// creating a JugglingDB model to back the resource,
11+
// allowing the resource to be instantiable and backed by a datasource
12+
//
13+
14+
function enable (r, options) {
15+
16+
if(typeof options === "string") {
17+
options = {
18+
type: options
19+
};
20+
}
21+
22+
//
23+
// Require JugglingDB.Schema
24+
//
25+
var Schema = require('./vendor/jugglingdb').Schema,
26+
uuid = require('node-uuid'),
27+
path = require('path');
28+
29+
//
30+
// Map uuid library to jugglingdb resource
31+
//
32+
jugglingdb.uuid = uuid;
33+
34+
//
35+
// Create new JugglingDB schema, based on incoming datasource type
36+
//
37+
var _type = mappings[options.type] || options.type || 'fs';
38+
var schema = new Schema(_type, {
39+
database: options.name || "big",
40+
host: options.host,
41+
port: options.port,
42+
path: options.path || path.join(resource.helper.appDir, 'db'),
43+
username: options.username,
44+
password: options.password,
45+
options: options.options,
46+
https: true // TODO: check that HTTPS actually does something
47+
});
48+
49+
//
50+
// Create empty schema object for mapping between resource and JugglingDB
51+
//
52+
var _schema = {};
53+
54+
//
55+
// For every property in the resource schema, map the property to JugglingDB
56+
//
57+
Object.keys(r.schema.properties).forEach(function(p){
58+
var prop = r.schema.properties[p];
59+
_schema[p] = { type: jugglingType(prop) };
60+
});
61+
function jugglingType(prop) {
62+
var typeMap = {
63+
'string': String,
64+
'number': Number,
65+
'integer': Number,
66+
'array': JSON,
67+
'boolean': Boolean,
68+
'object': JSON,
69+
'null': null,
70+
'any': String
71+
};
72+
73+
return typeMap[prop.type] || String;
74+
}
75+
76+
//
77+
// Create a new JugglingDB schema based on temp schema
78+
//
79+
var Model = schema.define(r.name, _schema);
80+
81+
//
82+
// Attach the CRUD methods to the resource
83+
//
84+
85+
//
86+
// CREATE method
87+
//
88+
function create (data, callback) {
89+
//
90+
// If no id is specified, create one using node-uuid
91+
//
92+
if(typeof data.id === 'undefined' || data.id.length === 0) {
93+
data.id = uuid();
94+
}
95+
96+
//
97+
// JugglingDB's "create" method can act like a "create or update"
98+
// depending on the adapter, even though JugglingDB has a separate code
99+
// path for "createOrUpdate" (for example, the cradle adapter has this
100+
// behavior). So, we use our internal "get" function to ensure that it does
101+
// not already exist.
102+
//
103+
// TODO: It is technically possible to have a uuid collision here,
104+
// though very unlikely.
105+
//
106+
get(data.id, function (err, result) {
107+
if (err) {
108+
//
109+
// "not found" errors are good in this case, so call create. Note that
110+
// this error message comes from the get function and not from
111+
// JugglingDB.
112+
//
113+
if (err.message.match(/not found/)) {
114+
return Model.create(data, callback);
115+
}
116+
117+
//
118+
// Other errors should be sent in the callback
119+
//
120+
return callback(err);
121+
}
122+
return callback(new Error(data.id + ' already exists'));
123+
});
124+
}
125+
r.method('create', create, {
126+
"description": "create a new " + r.name,
127+
"properties": {
128+
"options": {
129+
"type": "object",
130+
"properties": r.schema.properties
131+
},
132+
"callback": {
133+
"type": "function"
134+
}
135+
}
136+
});
137+
138+
//
139+
// Get method
140+
//
141+
function get (id, callback){
142+
// TODO: .all is now broken in fs adapter
143+
// NOTE: JugglingDB.find is really resource.get
144+
// NOTE: resource.get is JugglingDB.all with a filter
145+
Model.find(id, function(err, result){
146+
if(result === null) {
147+
return callback(new Error(id + ' not found'));
148+
}
149+
// TODO: check if any of the fields are keys, if so, fetch them
150+
callback(err, result);
151+
});
152+
}
153+
r.method('get', get, {
154+
"description": "get " + r.name + " by id",
155+
"properties": {
156+
"id": {
157+
"type": "any",
158+
"description": "the id of the object",
159+
"required": true
160+
},
161+
"callback": {
162+
"type": "function"
163+
}
164+
}
165+
});
166+
167+
//
168+
// Find method
169+
//
170+
function find (query, callback) {
171+
//
172+
// Remove any empty values from the query
173+
//
174+
for(var k in query) {
175+
if(query[k].length === 0) {
176+
delete query[k];
177+
}
178+
}
179+
180+
Model.all({ where: query }, function(err, results){
181+
if (!Array.isArray(results)) {
182+
results = [results];
183+
}
184+
callback(err, results);
185+
});
186+
}
187+
188+
var querySchema = {
189+
properties: {}
190+
}
191+
Object.keys(r.schema.properties).forEach(function(prop){
192+
if(typeof r.schema.properties[prop] === 'object') {
193+
querySchema.properties[prop] = {};
194+
for (var p in r.schema.properties[prop]) {
195+
querySchema.properties[prop][p] = r.schema.properties[prop][p];
196+
}
197+
} else {
198+
querySchema.properties[prop] = r.schema.properties[prop] || {};
199+
}
200+
querySchema.properties[prop].default = "";
201+
querySchema.properties[prop].required = false;
202+
//
203+
// TODO: remove the following two lines and make enum search work correctly
204+
//
205+
querySchema.properties[prop].type = "any";
206+
delete querySchema.properties[prop].enum;
207+
delete querySchema.properties[prop].format;
208+
});
209+
210+
r.method('find', find, {
211+
"description": "search for instances of " + r.name,
212+
"properties": {
213+
"options": {
214+
"type": "object",
215+
"properties": querySchema.properties
216+
},
217+
"callback": {
218+
"type": "function"
219+
}
220+
}
221+
});
222+
223+
//
224+
// All method
225+
//
226+
function all (callback) {
227+
Model.all({}, callback);
228+
}
229+
230+
r.method('all', all, {
231+
"description": "gets all instances of " + r.name,
232+
"properties": {
233+
"callback": {
234+
"type": "function"
235+
}
236+
}
237+
});
238+
239+
//
240+
// Update method
241+
//
242+
function update (options, callback){
243+
//
244+
// JugglingDB does not have a strict update and instead has
245+
// updateOrCreate, so do a get first and act accordingly
246+
//
247+
get(options.id, function (err, result) {
248+
if (err) {
249+
//
250+
// Unlike the case with strict create, "not found" errors mean we are
251+
// unable to do an update
252+
//
253+
return callback(err);
254+
}
255+
Model.updateOrCreate(options, function(err, updated){
256+
if(err) {
257+
return callback(err);
258+
}
259+
get(options.id, callback);
260+
});
261+
});
262+
}
263+
r.method('update', update, {
264+
"description": "updates a " + r.name + " by id",
265+
"properties": {
266+
"options": {
267+
"type": "object",
268+
"properties": r.schema.properties
269+
},
270+
"callback": {
271+
"type": "function"
272+
}
273+
}
274+
});
275+
276+
//
277+
// Update or create
278+
//
279+
function updateOrCreate (options, callback) {
280+
if(typeof options.id === 'undefined' || options.id.length === 0) {
281+
options.id = uuid();
282+
}
283+
284+
get(options.id, function(err, record){
285+
if (err) {
286+
Model.create(options, callback);
287+
} else {
288+
for (var p in options) {
289+
record[p] = options[p];
290+
}
291+
record.save(callback);
292+
}
293+
});
294+
295+
}
296+
r.method('updateOrCreate', updateOrCreate, {
297+
"description": "updates a " + r.name + " by id, and creates if necessary",
298+
"properties": {
299+
"options": {
300+
"type": "object",
301+
"properties": r.schema.properties
302+
},
303+
"callback": {
304+
"type": "function"
305+
}
306+
}
307+
});
308+
309+
//
310+
// Destroy method
311+
//
312+
function destroy (id, callback){
313+
Model.find(id, function(err, result){
314+
if (err) {
315+
return callback(err);
316+
}
317+
result.destroy(function(){
318+
callback(null, null);
319+
});
320+
});
321+
}
322+
r.method('destroy', destroy, {
323+
"description": "destroys a " + r.name + " by id",
324+
"properties": {
325+
"id": {
326+
"type": "string",
327+
"description": "the id of the object",
328+
"required": true
329+
},
330+
"callback": {
331+
"type": "function"
332+
}
333+
}
334+
});
335+
336+
// assign model to resource
337+
r.model = Model;
338+
}
339+
340+
//
341+
// Provider API mapping for JugglingDB to datasource API for convenience
342+
//
343+
var mappings = {
344+
"couch": "cradle",
345+
"couchdb": "cradle"
346+
};
347+
348+
jugglingdb.dependencies = {
349+
"node-uuid": "*",
350+
"async": "*"
351+
};
352+
353+
exports.jugglingdb = jugglingdb;
File renamed without changes.
File renamed without changes.
File renamed without changes.

memory/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ memory.method('start', function (cb){
77
cb(null, true);
88
});
99

10+
memory.method('enable', resource.use('jugglingdb').enable);
11+
1012
exports.memory = memory;

0 commit comments

Comments
 (0)