Comparing version 0.0.1-pre2 to 0.0.2
275
model.js
var Emitter = require('emitter'); | ||
var ajax = require('superagent'); | ||
var xtend = require('xtend'); | ||
var ArrayModel = require('./array_model'); | ||
var Model = function(opt) { | ||
var properties = Object.keys(opt); | ||
var Model = function(schema, opt) { | ||
opt = opt || {}; | ||
schema = schema || {}; | ||
var properties = Object.keys(schema); | ||
// sync function for CRUD | ||
var sync = opt.sync; | ||
var Construct = function(initial) { | ||
@@ -17,6 +23,35 @@ if (!(this instanceof Construct)) { | ||
var self = this; | ||
self.is_new = true; | ||
// default state is saved | ||
self._saved = true; | ||
// basepath for the url | ||
self.url_root = Construct.url_root; | ||
if (initial) { | ||
self.id = initial.id; | ||
} | ||
// url property can be used to overrride the model's url | ||
var _url = undefined; | ||
Object.defineProperty(self, 'url', { | ||
get: function() { | ||
// if user explicitly set, return their value | ||
if (_url) { | ||
return _url; | ||
} | ||
if (self.is_new()) { | ||
return self.url_root; | ||
} | ||
return self.url_root + '/' + self.id; | ||
}, | ||
set: function(val) { | ||
_url = val; | ||
} | ||
}); | ||
properties.forEach(function(prop) { | ||
var config = opt[prop]; | ||
var config = schema[prop]; | ||
@@ -28,4 +63,7 @@ var prop_val = (initial) ? initial[prop] : undefined; | ||
// shit... so in this case, we don't need a submodel | ||
// the issue is we have created a Model for each item | ||
// but, our model does not get the proper url rool | ||
if (typeof item === 'object') { | ||
prop_val = ArrayModel(Model(item), prop_val, self); | ||
prop_val = ArrayModel(Model(item, opt), prop_val, self); | ||
} | ||
@@ -37,22 +75,91 @@ else { | ||
if (prop_val && config instanceof Function && !(prop_val instanceof config)) { | ||
var is_constructor = (config instanceof Function); | ||
if (prop_val && is_constructor && !(prop_val instanceof config)) { | ||
prop_val = config(prop_val); | ||
} | ||
if (config.type === 'property') { | ||
// create an object wrapper, this lets us emit events | ||
// when internal properties are set | ||
function inner_obj(key_path, props, initial) { | ||
var properties = {}; | ||
initial = initial || {}; | ||
Object.keys(props).forEach(function(key) { | ||
var path = key_path + '.' + key; | ||
var value_holder = initial[key]; | ||
properties[key] = { | ||
enumerable: true, | ||
get: function() { | ||
return value_holder; | ||
}, | ||
set: function(val) { | ||
var old = value_holder; | ||
value_holder = val; | ||
self.emit('change ' + path, val, old); | ||
} | ||
} | ||
}); | ||
var proto = null; | ||
return Object.create(proto, properties); | ||
} | ||
if (config instanceof Function) { | ||
// see if it has keys | ||
Object.defineProperty(self, prop, { | ||
enumerable: false, | ||
get: config.get | ||
enumerable: true, | ||
get: function() { | ||
return prop_val; | ||
}, | ||
set: function(val) { | ||
var old = prop_val; | ||
// this handles the case of setting via same object | ||
// we don't need to call constructor | ||
if (val instanceof config) { | ||
prop_val = val | ||
} | ||
else { | ||
prop_val = config(val); | ||
} | ||
self._saved = false; | ||
self.emit('change ' + prop, prop_val, old); | ||
} | ||
}); | ||
function change_event() { | ||
self.emit('change ' + prop); | ||
return; | ||
} | ||
// user specified an inner object | ||
// but don't do this for arrays | ||
var keys = Object.keys(config); | ||
if ( !(config instanceof Array) && keys.length > 0) { | ||
// no value set by default | ||
if (prop_val) { | ||
prop_val = inner_obj(prop, config, prop_val); | ||
} | ||
config.depends.forEach(function(parent_prop) { | ||
self.on('change ' + parent_prop, change_event); | ||
// if the nothing above captured and config is a regular object | ||
// see if it has keys | ||
Object.defineProperty(self, prop, { | ||
enumerable: true, | ||
get: function() { | ||
return prop_val; | ||
}, | ||
set: function(val) { | ||
var old = prop_val; | ||
prop_val = inner_obj(prop, config, val); | ||
self._saved = false; | ||
self.emit('change ' + prop, prop_val, old); | ||
} | ||
}); | ||
return; | ||
} | ||
// if the nothing above captured and config is a single valueish | ||
Object.defineProperty(self, prop, { | ||
@@ -66,3 +173,4 @@ enumerable: true, | ||
prop_val = val; | ||
self.emit('change ' + prop, val, old); | ||
self._saved = false; | ||
self.emit('change ' + prop, prop_val, old); | ||
} | ||
@@ -75,2 +183,4 @@ }); | ||
Construct.url_root = opt.url_root; | ||
Construct.prototype.toJSON = function() { | ||
@@ -91,31 +201,41 @@ var self = this; | ||
// if the model has an ID property, then it is not considered new | ||
Construct.prototype.is_new = function() { | ||
var self = this; | ||
return !self.id; | ||
}; | ||
// return true if the model state has been persistent to the server | ||
// false for 'is_new()' or if a property has changed since last sync | ||
Construct.prototype.is_saved = function() { | ||
var self = this; | ||
return !self.is_new() && self._saved; | ||
}; | ||
Construct.prototype.save = function(cb) { | ||
var self = this; | ||
if (self.parent) { | ||
return self.parent.save(); | ||
} | ||
cb = cb || function() {}; | ||
if (self.is_new) { | ||
return ajax.post(self.url).send(self).end(function(err, res) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
var sync_opt = { | ||
url: self.url, | ||
method: 'PUT', | ||
body: self | ||
}; | ||
if (res.status !== 200) { | ||
return cb(new Error(res.body.error || 'failed to save')); | ||
} | ||
var is_new = self.is_new(); | ||
sync_opt.method = is_new ? 'POST' : 'PUT'; | ||
self.is_new = false; | ||
// id of the model should be the response | ||
self.id = res.body.id; | ||
self.url += '/' + self.id; | ||
sync(sync_opt, function(err, result) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
return cb(null); | ||
}); | ||
} | ||
// only expect id back if new | ||
// for updating existing we don't do this? | ||
if (is_new) { | ||
self.id = result.id; | ||
} | ||
ajax.put(self.url).send(self).end(function(err, res) { | ||
return cb(err); | ||
return cb(null); | ||
}); | ||
@@ -126,3 +246,14 @@ }; | ||
var self = this; | ||
ajax.get(self.url).end(function(err, res) { | ||
// nothing to fetch if we don't have an id | ||
if (!self.id) { | ||
return; | ||
} | ||
var sync_opt = { | ||
url: self.url_root + '/' + self.id, | ||
method: 'GET' | ||
}; | ||
sync(sync_opt, function(err, result) { | ||
if (err) { | ||
@@ -132,11 +263,8 @@ return cb(err); | ||
if (res.status !== 200) { | ||
return cb(new Error('failed to fetch')); | ||
// set our properties | ||
for (var key in result) { | ||
self[key] = result[key]; | ||
} | ||
var val = Construct(res.body); | ||
val.is_new = false; | ||
val.url = self.url; | ||
cb(null, val); | ||
return cb(null); | ||
}); | ||
@@ -148,7 +276,12 @@ }; | ||
// model was never saved to server | ||
if (self.is_new) { | ||
if (self.is_new()) { | ||
return; | ||
} | ||
ajax.del(self.url).end(function(err, res) { | ||
var sync_opt = { | ||
url: self.url, | ||
method: 'DELETE' | ||
}; | ||
sync(sync_opt, function(err) { | ||
if (err) { | ||
@@ -163,2 +296,54 @@ return cb(err); | ||
/// Class functions | ||
// get a single Model instance by id | ||
Construct.get = function(id, cb) { | ||
var self = this; | ||
var sync_opt = { | ||
url: self.url_root + '/' + id, | ||
method: 'GET' | ||
}; | ||
sync(sync_opt, function(err, result) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
return cb(null, Construct(result)); | ||
}); | ||
}; | ||
// query for a list of Models | ||
// @param [Object] query optional query object | ||
Construct.find = function(query, cb) { | ||
var self = this; | ||
if (typeof query === 'function') { | ||
cb = query; | ||
query = {} | ||
} | ||
var sync_opt = { | ||
url: self.url_root, | ||
query: query, | ||
method: 'GET' | ||
}; | ||
sync(sync_opt, function(err, result) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
return cb(null, result.map(Construct)); | ||
}); | ||
}; | ||
// copy this model and optionally mixin some new shit | ||
Construct.extend = function(more_schema, more_opt) { | ||
more_schema = more_schema || {}; | ||
more_opt = more_opt || {}; | ||
return Model(xtend(schema, more_schema), xtend(opt, more_opt)); | ||
}; | ||
return Construct; | ||
@@ -165,0 +350,0 @@ }; |
{ | ||
"name": "bamboo", | ||
"version": "0.0.1-pre2", | ||
"version": "0.0.2", | ||
"author": "Roman Shtylman <shtylman@gmail.com>", | ||
"dependencies": { | ||
"emitter-component": "1.0.1", | ||
"superagent": "shtylman/superagent#0.15.4-dz0" | ||
"emitter-component": "1.1.0", | ||
"superagent": "0.15.7", | ||
"xtend": "2.1.1" | ||
}, | ||
"scripts": { | ||
"test": "zuul test/index.js", | ||
"test-local": "zuul --local 8080 -- test/index.js" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/defunctzombie/bamboo.git" | ||
}, | ||
"main": "./index.js", | ||
"devDependencies": { | ||
"zuul": "1.0.4", | ||
"mocha": "1.14.0", | ||
"express": "3.4.5", | ||
"after": "0.8.1", | ||
"uuid": "1.4.1" | ||
}, | ||
"browser": { | ||
@@ -10,0 +27,0 @@ "emitter": "emitter-component" |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
GitHub dependency
Supply chain riskContains a dependency which resolves to a GitHub URL. Dependencies fetched from GitHub specifiers are not immutable can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
28267
16
723
1
135
0
3
5
1
+ Addedxtend@2.1.1
+ Addedcookiejar@1.3.0(transitive)
+ Addeddebug@0.7.4(transitive)
+ Addedemitter-component@1.0.01.1.0(transitive)
+ Addedformidable@1.0.14(transitive)
+ Addedmethods@0.0.1(transitive)
+ Addedmime@1.2.5(transitive)
+ Addedobject-keys@0.4.0(transitive)
+ Addedqs@0.6.5(transitive)
+ Addedreduce-component@1.0.1(transitive)
+ Addedsuperagent@0.15.7(transitive)
+ Addedxtend@2.1.1(transitive)
- Removedemitter-component@1.0.1(transitive)
Updatedemitter-component@1.1.0
Updatedsuperagent@0.15.7