├── .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 |
3 | <% messages().forEach(function(msg){ %>
4 | - <%- msg %>
5 | <% }) %>
6 |
7 | <% } %>
--------------------------------------------------------------------------------
/views/user/index.html:
--------------------------------------------------------------------------------
1 | Users
2 | <%- partial('../messages') %>
3 |
--------------------------------------------------------------------------------
/views/user/show.html:
--------------------------------------------------------------------------------
1 | Viewing user #<%= user.id %>
2 | <%- partial('../messages') %>
3 |
4 | - <%- user.name %>
5 | - <%- user.email %>
6 | - Edit this user
7 |
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------