WebFund 2013F Final Exam Review

From Soma-notes

To prepare for the final exam you should study the following node application derived from the solutions to Assignment 4. This code is also available in a zip file and in an unpacked directory. Consider the following:

  • What does each line do? Specifically, what errors/loss of functionality would you lose if you remove any line?
  • What node functionality has been omitted from this application relative to the other ones we have covered this term?
  • How would you get this application to run?
  • Where are functions being defined? Called?
  • What is the control flow of this application? Specifically, what code is executed, when, and what HTML and HTTP traffic does it produce? You should be able to trace how requests go from the initial loading of a page (via an HTTP GET), to functions being executed in the server, to an HTML or other document being sent back to the client which may then execute client-side JavaScript code that will then generate further HTTP GETs and POSTs. When discussing what code is executed you should specify individual function calls.

Good luck!

small-adventure2/app.js

var express = require('express');
var routes = require('./routes');
var http = require('http');
var app = express();

app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.cookieParser('COMP2406 is over!'));
app.use(express.session());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
app.use(express.errorHandler());

app.get('/', routes.index);
app.get('/game', routes.game);
app.get('/getContents', routes.getContents);
app.post('/start', routes.start);
app.post('/quit', routes.quit);
app.post('/doAction', routes.doAction);

http.createServer(app).listen(app.get('port'), function(){
    console.log('Express server listening on port ' + app.get('port'));
});

small-adventure2/routes/index.js

var sanitize = require('validator').sanitize;
var mongoose = require('mongoose');
var db = mongoose.connection;
var Room;

mongoose.connect('mongodb://localhost/2406rooms');
db.on('error', console.error.bind(console, 'MongoDB connection error:'));

var setupRoomsDB = function() {
    var roomSchema = mongoose.Schema({
        name: {type: String, required: true},
        title: {type: String, required: true},
        description: {type: String, required: true},
        roomExits: {type: [String], required: true}
    });
    Room = mongoose.model("Room", roomSchema);
}
db.once('open', setupRoomsDB);

exports.index = function(req, res) {
    if (req.session.player) {
        res.redirect("/game");
    } else {
        res.render('index', { title: 'COMP 2406 Adventure Demo', 
                              error: req.query.error });
    }
}

exports.start = function(req, res) {
    var player = req.body.player;

    req.session.player = sanitize(player).escape();
    req.session.currentRoom = "start";
    res.redirect("/game")
}

exports.quit = function(req, res) {
    req.session.destroy(function(err){
        if(err){
            console.log("Error: %s", err);
        }
    });
    res.redirect("/");
}

exports.game = function(req, res) {
    if (req.session.player) {
        res.render("room.jade", {title: "Adventure Demo"});
    } else {
        res.redirect("/");
    }
}

exports.doAction = function(req, res) {
    var action = req.body.action;
    var room = req.body.room;

    if (action === "move") {
        req.session.currentRoom = room;
        res.send("Success");
    } else {
        res.send("Error: InvalidAction");
    }
}

exports.getContents = function(req, res) {
    Room.findOne({ name: req.session.currentRoom },
                 function(err, theRoom) {
                     if (!err && theRoom) {
                         theRoom.description = 
                             theRoom.description.replace("<?player>",
                                                         req.session.player);
                         res.send(theRoom);
                     } else {
                         res.send({name: "unknown",
                                   title: "Unknown room",
                                   description: "error finding room",
                                   roomExits: []});
                     }
                 });
}

small-adventure2/public/javascripts/adventure.js

(function() {
    var getRoom = function() {
        $.getJSON("/getContents", function(room) {
            var exits;
            $('#title').replaceWith('<h1 id="title">'+room.title+'</h1>');
            $('#description').replaceWith('<p id="description">' +
                                          room.description + '</p>');
            $('.exits').remove();
            room.roomExits.forEach(function(theExit) {
                $('#exitList').append(
                    '<button type="button" id="' + theExit +
                        '" class="btn btn-default exits">'
                        + theExit + '</button>');
                $('#'+theExit).on('click', function() {
                    $.post("/doAction", {action: "move",
                                         room: theExit});
                    getRoom();
                });
            });
        });     
    }
    $(window).load(getRoom());
})();

small-adventure2/views/layout.jade

doctype 5
html
  head
    title= title
    script(src='/libs/jquery/jquery.min.js')
    link(rel='stylesheet', href='/libs/bootstrap/css/bootstrap.min.css')
    block header
  body
    block content

small-adventure2/views/index.jade

extends layout

block content
  h1= title
  p Please choose your player name
  div
    form(action="/start", method="post")
        div.control-group.input-append
            input(type="text", name="player")
            label.add-on(for="player") Player Name
        button(type="submit") Start

small-adventure2/views/room.jade

extends layout

block header
  script(src='/javascripts/adventure.js')

block content
  h1(id="title") Room Name
  p(id="description") Room Description
  p Go to:
  div(id="exitList").btn-group
    button(type="button").btn.btn-default.exits Exits
  p
  form(action="/quit", method="post")
     button(type="submit") Quit