Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Client rendering -> File Uploads #38

Open
wants to merge 13 commits into
base: types/file
Choose a base branch
from
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Maki is a framework for hand-rolling web applications in a way that makes sense
- **REST API built-in.** All URIs are semantic and indempotent, automatically.
- **Client-Responsiveness.** Don't rely on user-agents or referers, but instead respond to what the client supports. Make a request that only accepts JSON? Get JSON back. Accept HTML? Get HTML back.
- **Javascript _optional_.** If the client doesn't have Javascript enabled, applications built with Maki will continue to work using pure HTML. When Javascript _is_ available, a performant and well-designed client-side application takes over to eliminate full-page loads. See also [Modules](#modules).
- **Connect Middlwares** Maki uses the [Connect middleware pattern](http://stephensugden.com/middleware_guide/) for extending core functionality, but extends the concept to apply to more than just HTTP servers.

## Quick Start
You'll need [node.js](http://nodejs.org) to build a Maki application. Additionally, [MongoDB](http://mongodb.org) and [Redis](http://redis.org) are the default storage and messaging engines, so you will need to install and configure them to use the defaults, or override them if you'd like to use something different.
Expand Down
5 changes: 3 additions & 2 deletions app/views/layouts/default.jade
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ html.no-js
script(src="/js/jquery.js")
script(src="/js/semantic.min.js")
script(src="/js/lodash.min.js")
script(src="/js/lokijs.min.js")

script(src="/js/templates.js")
script(src="/js/highlight.pack.js")
script(src="/js/headroom.min.js")
script(src="/js/page.js")
script(src="/js/uuid.js")
script(src="/js/jsonrpc.js")
script(src="/js/json-patch-duplex.min.js")

script(src="/js/maki.js")

Expand Down
25 changes: 24 additions & 1 deletion app/views/modules/collection.jade
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,42 @@
table.ui.table.segment.sortable
thead
tr
td
input(type="checkbox")
each field in fields
td #{field}
td

tbody
each item in collection
tr
tr(data-model="#{resource.name}", data-id="#{item[ idField ]}")
td
input(type="checkbox")
each field in fields
td
if (field == idField)
a(href="/#{resource.collection}/#{item[ field ]}") #{item[ field ]}
else
| #{item[ field ]}
td
i.link.icon.remove(data-action="remove-item")

tfoot
tr
td #{collection.length} items

script.
$(window).load(function() {
$('*[data-action=remove-item]').click(function( e ) {
e.preventDefault();

var $item = $( this ).parent().parent();

console.log( $item );

$.ajax({ method: 'delete', url: '/#{resource.collection}/' + $item.data('id') , success: function(data) {
console.log(data);
} });
return false;
})
});
4 changes: 2 additions & 2 deletions examples/bitfaucet/bitfaucet.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ Faucet.post('init', function( faucet , next ) {
// Before saving a Pour, validate that it is authorized.
// In our simple example, all Pours are allowed (without a rate-limit), but in a
// real-world example you will clearly want to implement some sort of validator.
Pour.pre('save', function( done ) {
Pour.pre('create', function( next ) {
var pour = this;
console.log('TODO: implement ratelimiter. pre-save() called.', pour );
done();
next();
});

// ## Resource Events
Expand Down
12 changes: 11 additions & 1 deletion lib/Maki.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,19 @@ var Maki = function( config ) {

};

Maki.prototype.define = function( name , options ) {
Maki.prototype.define = function( name , options , internals ) {
var self = this;
if (!name) throw new Error('"name" is required.');

if (internals) {
Object.keys( internals ).forEach(function( i ) {
var internal = internals[ i ];
var exec = 'var ' + i + ' = internal';
// TODO: evaluate a real, internal architecture method for this.
// evaluate the eval. evaluate. eval. evil.
eval( exec );
});
}

var resource = new self.Resource( name , options );

Expand Down
111 changes: 79 additions & 32 deletions lib/Resource/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
var util = require('util');
var pluralize = require('pluralize');
var pathToRegex = require('path-to-regexp');
var transform = require('jsonpath-object-transform');
var _ = require('lodash');
var async = require('async');

var Resource = function( name , options ) {
var self = this;
Expand All @@ -13,6 +15,7 @@ var Resource = function( name , options ) {
if (!options.routes) options.routes = {};
if (!options.methods) options.methods = {};
if (!options.templates) options.templates = {};
if (!options.internals) options.internals = {};
if (!options.plugins) options.plugins = [];
if (!options.requires) options.requires = [];
if (!options.handlers) options.handlers = {};
Expand Down Expand Up @@ -77,12 +80,33 @@ var Resource = function( name , options ) {
if ( attribute.id === true ) {
self.fields.id = f;
delete self.keys._id; //Remove the _id field from white-list

// this is an indicator to create an index on this field;
// used for client-side d
self.options.attributes[ f ].index = true;
}
if ( attribute.name === true ) {
self.fields.name = f;
}

});

self.middlewares = {
pre: {
query: [],
get: [],
create: [],
update: [],
destroy: []
},
post: {
query: [],
get: [],
create: [],
update: [],
destroy: []
}
};

// build the routes for this resource (passed to generate regexes)
self.routes = {
Expand Down Expand Up @@ -163,39 +187,34 @@ Resource.prototype.attach = function( maki ) {
if (self.internal || self.static) return;

maki.resources[ self.name ] = self;
}
};

Resource.prototype.take = function( obj , val ) {
return obj[ val ];
};

Resource.prototype.supermap = function( obj ) {

};

Resource.prototype.transform = function( input , rules ) {
return transform( input , rules );
};

// TODO: dry with post
Resource.prototype.pre = function( event , binding ) {
var self = this;

// TODO: use maki-specific bindings
var supportedEvents = ['init', 'validate', 'save', 'remove'];
if ( supportedEvents.indexOf( event ) < 0 ) throw new Error('not a valid event');

self.Schema.pre( event , true , function( next , done ) {
var self = this;
binding.apply( self, [ function() {
next();
done();
} ] );
});
if (!self.middlewares['pre'][ event ]) throw new Error('not a valid event');
self.middlewares['pre'][ event ].push( binding );
return self;
}

// TODO: dry with pre
Resource.prototype.post = function( event , binding ) {
var self = this;

// TODO: use maki-specific bindings
var supportedEvents = ['init', 'validate', 'save', 'remove'];
if ( supportedEvents.indexOf( event ) < 0 ) throw new Error('not a valid event');

self.Schema.post( event , function( doc ) {
var self = this;
binding.apply( self, [ doc , function(err) {
if (err) console.log(err);
} ] );
});
if (!self.middlewares['post'][ event ]) throw new Error('not a valid event');
self.middlewares['post'][ event ].push( binding );
return self;
}

Resource.prototype.path = function( name ) {
Expand Down Expand Up @@ -231,25 +250,48 @@ Resource.prototype.get = function( q , complete ) {
if (!complete) var complete = new Function();
Model.findOne( q ).exec(function(err, doc) {
if (err) return complete(err);
if (!doc) return complete( 404 );
return complete( err , doc );
});

}

Resource.prototype.create = function( doc , complete ) {
var self = this;
if (!complete) var complete = new Function();
if (self.options.static) return complete();

var Model = self.Model;

var query = {};
query[ self.fields.id ] = doc[ self.fields.id ];

if (!complete) var complete = new Function();
Model.findOne( doc ).exec(function(err, instance) {
Model.findOne( query ).exec(function(err, instance) {
if (err) return complete(err);
if (instance) return complete( err , instance );

var instance = new Model( doc );
instance.save(function(err) {
self.emit('create', instance );
complete( err , instance );

async.series( self.middlewares['pre']['create'].map(function(x) {
return function( done ) {
x.apply( instance , [ done ] );
};
}), function( err , results ) {
if (err) return complete(err);

instance.save(function(err) {
if (err) return complete(err);

async.series( self.middlewares['post']['create'].map(function(x) {
return function( done ) {
x.apply( instance , [ done ] );
};
}), function( err , results ) {
self.emit('create', instance );
return complete( err , instance );
});

});
});
});
}
Expand Down Expand Up @@ -282,12 +324,17 @@ Resource.prototype.update = function( query , params , complete ) {
});
}

Resource.prototype.destroy = function( id , complete ) {
if (self.options.static) return complete();

Resource.prototype.destroy = function( q, complete ) {
var self = this;
if (self.options.static) return complete();

var Model = self.Model;

Model.remove( q , function(err, num) {
console.log('remove', q , 'generated', err , 'and' , num );
complete( err );
});

return;

}
Expand Down
Loading