Permalink
Browse files

Moved jade files into subfolders for clarity

Added Schema class with user and product schemas
Implemented db field validation and parsing within the schema
Implemented MongoDB lib for users and products
Implemented collection class a wrapper to mongodb for common tasks
Implemented product publishing form
  • Loading branch information...
1 parent 274edef commit 66365befca05cc5bdd8135626260ebeaa18adf28 @flesler committed Jan 30, 2012
View
@@ -4,4 +4,5 @@ config.json
node.exe.stackdump
_assets
npm-debug.log
-_propuestas
+_propuestas
+_temp
View
3 app.js
@@ -1,5 +1,4 @@
var express = require('express'),
- db = require('./lib/db'),
config = require('./cfg/config.json');
// Import to ensure the global function _ is set
@@ -40,7 +39,7 @@ app.configure('production', function(){
// DB
-db.init(config.db);
+require('./lib/db')(config.db);
// Routes
View
@@ -1,6 +1,7 @@
{
"port":8080,
"db": {
+ "name": "art-store",
"address":"127.0.0.1",
"port":27017
}
View
@@ -1,9 +1,19 @@
-exports.init = function(config) {
- // TODO
-};
+// Docs at: https://github.com/christkv/node-mongodb-native/tree/master/docs
+var mongo = require('mongodb');
-exports.schemas = require('./schemas');
+exports = module.exports = function(config) {
+ var server = new mongo.Server(config.address, config.port, {});
+ var db = new mongo.Db(config.name, server, {});
-exports.users = require('./users');
+ db.on('close', function(err){
+ console.warn('DB was closed');
+ });
-exports.products = require('./products');
+ require('./collection')(db);
+
+ exports.schemas = require('./schemas');
+
+ exports.users = require('./users');
+
+ exports.products = require('./products');
+};
View
@@ -1,62 +1,29 @@
-// I temporary store on memory until the DB is implemented
-var products = [
- {id:0, user:'test', title:'test1', price:123, type:'pintura', props:{size:'20x25', color:'rojo', medium:'oleo'}},
- {id:1, user:'test', title:'test2', price:111, type:'pintura', props:{size:'20x25', color:'azul', medium:'acrilico'}},
- {id:2, user:'test', title:'test3', price:321, type:'pintura', props:{size:'30x35', color:'amarillo', medium:'acuarelas'}}
-];
+var schema = require('./schemas').product;
+var collection = new (require('./collection').Collection)('products', schema);
-var FIELDS = ['user', 'title', 'price', 'type', 'props'];
-
-exports.byId = function(id, next) {
- var prod = products[id];
- if (!prod) {
- var err = new Error('products not found: '+id);
- }
-
- next(err, prod);
+exports.byId = function(id, callback) {
+ collection.byId(id, callback);
};
-exports.byUser = function(id, next) {
- var prods = products.filter(function(prod){
- return prod.user === id;
- });
- next(null, prods);
+exports.byUser = function(id, callback) {
+ // TODO: Can only send some fields?
+ collection.find({owner:id}, callback);
};
-
-exports.filter = function(type, filter, next) {
- var prods = products.filter(function(prod){
- var prop = prod.props;
- if (prod.type !== type) {
- return false;
- }
- for (var key in filter) {
- if (filter[key] != prod.props[key]) {
- return false;
- }
- }
- return true;
- });
- console.log(prods)
- next(null, prods);
-};
-
-exports.add = function(prod, next) {
- for (var i = 0; i < FIELDS.length; i++) {
- var field = FIELDS[i];
- if (!prod[field]) {
- return next(new Error('missing field: '+field));
- }
+exports.filter = function(category, filter, callback) {
+ var query = {category:category};
+ for (var key in filter) {
+ query['props.'+key] = filter[key];
}
+ collection.find(query, callback);
+};
- // TODO: Validate user fields that are not to be registered
-
- var id = prod.id = products.length;
-
- products[id] = prod;
- next(null, prod);
+exports.add = function(prod, callback) {
+ collection.insert(prod, callback);
};
-exports.list = function(next) {
- next(null, products.concat());
+exports.list = function(callback) {
+ collection.list({
+ fields:{_id:1, title:1}
+ }, callback);
};
View
@@ -1,2 +1,205 @@
-// TODO
-exports.User = {};
+// Poor man's implementation of Mongoose's Schemas
+// Currently can't seem to be able to install mongoose (windows)
+// So I'll use this for now
+
+
+//- Schema Class
+
+function Schema(name, fields) {
+ this.name = name;
+ this.fields = fields;
+ this.initFields();
+}
+
+Schema.prototype = {
+
+ initFields:function(fields){
+ var fs = this.fields;
+ for (var key in fs) {
+ if (typeof fs[key] !== 'object') {
+ fs[key] = {type:fs[key]};
+ }
+
+ if (!fs[key].type) {
+ throw this.error('Field '+key+' has no type');
+ }
+
+ if (fs[key].isId) {
+ if (this.idField) {
+ throw this.error('More than one is field found');
+ }
+ this.idField = key;
+ }
+ }
+ },
+
+ error:function(msg) {
+ return new Error(this.name + ' > ' + msg);
+ },
+
+ // Call when saving to db
+ parseRecord:function(record, partial) {
+ var parsed = {};
+
+ for (var key in this.fields) {
+ var field = this.fields[key];
+ var orig = record[key];
+
+ // Default value for field
+ if (isInvalid(orig)) {
+ var def = field.default;
+ if (typeof def === 'function') {
+ orig = def();
+ } else {
+ orig = def;
+ }
+
+ if (isInvalid(orig)) {
+ if (partial) continue;
+ if (field.optional) continue;
+
+ return this.error(key + ' field is missing');
+ }
+ }
+
+ // Type checking/casting
+ var v = parse(orig, field.type);
+
+ if (v instanceof Error) {
+ return this.error(key + ' > ' + v.message);
+ }
+
+ if (isInvalid(v)) {
+ return this.error('Invalid '+key);
+ }
+
+ // Field record saved as id
+ if (field.isId) {
+ parsed._id = v;
+ } else {
+ parsed[key] = v;
+ }
+ }
+
+ return parsed;
+ },
+
+ // Call when fecthing from db
+ restoreRecord:function(record) {
+ // Support null records
+ if (record && '_id' in record) {
+ var newIdKey = this.idField || 'id';
+ record[newIdKey] = record._id;
+ delete record._id;
+ }
+ return record;
+ },
+
+ restoreRecords:function(records) {
+ records.forEach(this.restoreRecord, this);
+ return records;
+ }
+};
+
+//- Utils
+
+function parse(val, type) {
+ if (type instanceof Schema) {
+ return type.parseRecord(val);
+ }
+
+ if (typeof type === 'function') {
+ return type(val);
+ }
+
+ return val;
+};
+
+function isInvalid(val) {
+ return val == null || val === NaN;
+};
+
+//- Custom Types
+
+var Regex = function(regex) {
+ return function(str) {
+ return regex.test(str) ? str : null;
+ };
+};
+
+var Email = Regex(/[^@]+@[^.@]+(?:\.\w{2,6}){1,3}/); // This regex is temp, will write a better one
+
+var DateType = function(val) {
+ var d = new Date(val);
+ return d.toString() === 'Invalid Date' ? null : d;
+};
+
+var RealString = function(str) {
+ return String(str) || null; // Rejects empty strings
+};
+
+var PositiveNumber = function(num) {
+ num = Number(num);
+ return isNaN(num) || num < 0 ? null : num;
+};
+
+var ArrayOf = function(type) {
+ return function(arr) {
+ if (arr instanceof Array === false) {
+ return null;
+ }
+ for (var i = 0; i < arr.length; i++) {
+ var val = parse(arr[i], type);
+ if (isInvalid(val)) {
+ return new Error('Invalid member #'+i+':'+arr[i]);
+ }
+ arr[i] = val;
+ }
+ return arr;
+ };
+};
+
+var ObjectOf = function(type) {
+ return function(obj) {
+ for (var key in obj) {
+ var val = parse(obj[key], type);
+ if (isInvalid(val)) {
+ return new Error('Invalid member '+key+':'+obj[key]);
+ }
+ obj[key] = val;
+ }
+ return obj;
+ };
+};
+
+//- Defaults
+
+var now = function(){
+ return new Date();
+};
+
+//- Internal Schemas
+
+var Category = new Schema('Category', {
+ name:RealString,
+ filters:ObjectOf(ArrayOf(RealString))
+});
+
+//- Exposed Schemas
+
+exports.user = new Schema('User', {
+ username:{type:RealString, isId:true},
+ pass:RealString,
+ name:RealString,
+ email:Email,
+ created:{type:DateType, default:now}
+});
+
+exports.product = new Schema('Product', {
+ title:RealString,
+ price:PositiveNumber,
+ category:RealString, // TODO: Check is a category
+ owner:RealString,
+ props:ObjectOf(RealString), // TODO: Should validate is a filter id
+ created:{type:DateType, default:now}
+});
Oops, something went wrong.

0 comments on commit 66365be

Please sign in to comment.