├── .gitignore ├── package.json ├── views ├── 404.html ├── 500.html ├── messages.html ├── user │ ├── index.html │ ├── show.html │ └── edit.html ├── layout.html └── app │ └── index.html ├── controllers ├── app.js └── user.js ├── server.js ├── public ├── Quotes.js └── style.css └── mvc.js /.gitignore: -------------------------------------------------------------------------------- 1 | .nodester.appconfig 2 | node_modules/ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"learn", 3 | "node":"0.6.12", 4 | "author":"proloser" 5 | } -------------------------------------------------------------------------------- /views/404.html: -------------------------------------------------------------------------------- 1 |

Cannot find <%= request.url %>

2 |

Sorry we failed to locate this page or resource.

-------------------------------------------------------------------------------- /controllers/app.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | // / 5 | 6 | index: function(req, res){ 7 | res.render(); 8 | } 9 | }; -------------------------------------------------------------------------------- /views/500.html: -------------------------------------------------------------------------------- 1 |

Internal Server Error

2 |

Sorry an error has occurred, please try refreshing the page or navigate back to home.

-------------------------------------------------------------------------------- /views/messages.html: -------------------------------------------------------------------------------- 1 | <% if (hasMessages) { %> 2 | 7 | <% } %> -------------------------------------------------------------------------------- /views/user/index.html: -------------------------------------------------------------------------------- 1 |

Users

2 | <%- partial('../messages') %> 3 | -------------------------------------------------------------------------------- /views/user/show.html: -------------------------------------------------------------------------------- 1 |

Viewing user #<%= user.id %>

2 | <%- partial('../messages') %> 3 | 8 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var express = require('express'); 7 | 8 | var app = express.createServer(); 9 | 10 | require('./mvc').boot(app); 11 | 12 | app.listen(15391); 13 | console.log('Express app started on port 15391'); -------------------------------------------------------------------------------- /views/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Express - MVC Example 4 | 5 | 6 | 7 | 8 | 9 | <%- body %> 10 | 11 | -------------------------------------------------------------------------------- /public/Quotes.js: -------------------------------------------------------------------------------- 1 | function Quotes($xhr) { 2 | var scope = this; 3 | scope.newRecord = {}; 4 | scope.quotes = []; 5 | $xhr('GET', 'users.json', function(code, response){ 6 | scope.quotes = response; 7 | }); 8 | scope.delete = function(index) { 9 | scope.quotes.splice(index,1); 10 | }; 11 | scope.save = function(newRecord) { 12 | scope.quotes.push(newRecord); 13 | scope.newRecord = {}; 14 | }; 15 | } -------------------------------------------------------------------------------- /views/user/edit.html: -------------------------------------------------------------------------------- 1 |

Editing user <%= user.name %>

2 | <%- partial('../messages') %> 3 |
4 | 5 |

Name:

6 |

Email:

7 |

8 |
-------------------------------------------------------------------------------- /public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 30px; 3 | font: 12px/1.4 "Helvetica Nueue", "Lucida Grande", Arial, sans-serif; 4 | } 5 | a { 6 | color: #00aaff; 7 | text-decoration: none; 8 | } 9 | a:hover { 10 | text-decoration: underline; 11 | } 12 | #messages { 13 | width: 95%; 14 | margin-bottom: 15px; 15 | padding: 10px; 16 | border: 1px solid #00DD00; 17 | -webkit-box-shadow: 1px 1px 4px rgba(0,0,0,0.2); 18 | } 19 | #messages li { 20 | list-style: none; 21 | } 22 | #messages em { 23 | font-weight: bold; 24 | } 25 | table { 26 | 27 | border-left: 1px solid #ccc; 28 | border-top: 1px solid #ccc; 29 | } 30 | td, th { 31 | border-right: 1px solid #ccc; 32 | border-bottom: 1px solid #ccc; 33 | padding: 10px; 34 | } -------------------------------------------------------------------------------- /views/app/index.html: -------------------------------------------------------------------------------- 1 |

Checkout my quotes

2 |
3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
IDRankQuoteAction
{{row.bash_id}}{{row.rank}}{{row.quote | html:'unsafe'}}Delete
24 |
25 |
-------------------------------------------------------------------------------- /controllers/user.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'), 2 | Schema = mongoose.Schema; 3 | 4 | mongoose.connect('mongodb://learn:node@flame.mongohq.com:27060/learn'); 5 | 6 | var Quote = new Schema(); 7 | 8 | Quote.add({ 9 | bash_id : { type: Number, index: { unique: true } } 10 | , quote : { type: String, required: true } 11 | , rank : Number 12 | }); 13 | 14 | /** 15 | * Define model. 16 | */ 17 | 18 | var quote = mongoose.model('Quote', Quote); 19 | 20 | module.exports = { 21 | 22 | // /users 23 | 24 | index: function(req, res, next){ 25 | quote.find({}, {}, {limit:10}, function(err, docs){ 26 | res.render(docs); 27 | }); 28 | }, 29 | 30 | // /users/:id 31 | 32 | show: function(req, res, next){ 33 | quote.findOne({bash_id: req.params.id}, function(err, docs){ 34 | res.render(docs); 35 | }); 36 | }, 37 | 38 | create: function(req, res, next){ 39 | var newQuote = new quote(req.body); 40 | newQuote.save(function(err, docs){ 41 | res.render(201); 42 | }); 43 | }, 44 | 45 | // /users/:id/edit 46 | 47 | edit: function(req, res, next){ 48 | get(req.params.id, function(err, user){ 49 | if (err) return next(err); 50 | res.render(user); 51 | }); 52 | }, 53 | 54 | // PUT /users/:id 55 | 56 | update: function(req, res, next){ 57 | var id = req.params.id; 58 | get(id, function(err){ 59 | if (err) return next(err); 60 | var user = users[id] = req.body.user; 61 | user.id = id; 62 | req.flash('info', 'Successfully updated _' + user.name + '_.'); 63 | res.redirect('back'); 64 | }); 65 | } 66 | }; -------------------------------------------------------------------------------- /mvc.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var fs = require('fs') 7 | , express = require('express'); 8 | 9 | exports.boot = function(app){ 10 | bootApplication(app); 11 | bootControllers(app); 12 | }; 13 | 14 | // App settings and middleware 15 | 16 | function bootApplication(app) { 17 | app.use(express.logger(':method :url :status')); 18 | app.use(express.bodyParser()); 19 | app.use(express.methodOverride()); 20 | app.use(express.cookieParser()); 21 | app.use(express.session({ secret: 'keyboard cat' })); 22 | app.use(app.router); 23 | app.use(express.static(__dirname + '/public')); 24 | 25 | // Example 500 page 26 | app.use(function(err, req, res, next){ 27 | res.render('500'); 28 | }); 29 | 30 | // Example 404 page via simple Connect middleware 31 | app.use(function(req, res){ 32 | res.render('404'); 33 | }); 34 | 35 | // Setup ejs views as default, with .html as the extension 36 | app.set('views', __dirname + '/views'); 37 | app.register('.html', require('ejs')); 38 | app.set('view engine', 'html'); 39 | 40 | // Some dynamic view helpers 41 | app.dynamicHelpers({ 42 | request: function(req){ 43 | return req; 44 | }, 45 | 46 | hasMessages: function(req){ 47 | if (!req.session) return false; 48 | return Object.keys(req.session.flash || {}).length; 49 | }, 50 | 51 | messages: function(req){ 52 | return function(){ 53 | var msgs = req.flash(); 54 | return Object.keys(msgs).reduce(function(arr, type){ 55 | return arr.concat(msgs[type]); 56 | }, []); 57 | } 58 | } 59 | }); 60 | } 61 | 62 | // Bootstrap controllers 63 | 64 | function bootControllers(app) { 65 | fs.readdir(__dirname + '/controllers', function(err, files){ 66 | if (err) throw err; 67 | files.forEach(function(file){ 68 | bootController(app, file); 69 | }); 70 | }); 71 | } 72 | 73 | // Example (simplistic) controller support 74 | 75 | function bootController(app, file) { 76 | var name = file.replace('.js', '') 77 | , actions = require('./controllers/' + name) 78 | , plural = name + 's' // realistically we would use an inflection lib 79 | , prefix = '/' + plural; 80 | 81 | // Special case for "app" 82 | if (name == 'app') prefix = '/'; 83 | 84 | Object.keys(actions).map(function(action){ 85 | var fn = controllerAction(name, plural, action, actions[action]); 86 | switch(action) { 87 | case 'index': 88 | app.get(prefix + '.:format?', fn); 89 | break; 90 | case 'show': 91 | app.get(prefix + '/:id.:format?', fn); 92 | break; 93 | case 'add': 94 | app.get(prefix + '/add', fn); 95 | break; 96 | case 'create': 97 | app.post(prefix, fn); 98 | break; 99 | case 'edit': 100 | app.get(prefix + '/:id/edit', fn); 101 | break; 102 | case 'update': 103 | app.put(prefix + '/:id', fn); 104 | break; 105 | case 'destroy': 106 | app.del(prefix + '/:id', fn); 107 | break; 108 | } 109 | }); 110 | } 111 | 112 | // Proxy res.render() to add some magic 113 | 114 | function controllerAction(name, plural, action, fn) { 115 | return function(req, res, next){ 116 | var render = res.render 117 | , format = req.params.format 118 | , path = __dirname + '/views/' + name + '/' + action + '.html'; 119 | res.render = function(obj, options, fn){ 120 | res.render = render; 121 | // Template path 122 | if (typeof obj === 'string') { 123 | return res.render(obj, options, fn); 124 | } 125 | 126 | // Format support 127 | if (format) { 128 | if (format === 'json') { 129 | return res.send(obj); 130 | } else { 131 | throw new Error('unsupported format "' + format + '"'); 132 | } 133 | } 134 | 135 | // Render template 136 | res.render = render; 137 | options = options || {}; 138 | // Expose obj as the "users" or "user" local 139 | if (action == 'index') { 140 | options[plural] = obj; 141 | } else { 142 | options[name] = obj; 143 | } 144 | return res.render(path, options, fn); 145 | }; 146 | fn.apply(this, arguments); 147 | }; 148 | } --------------------------------------------------------------------------------