WebFund 2014W Midterm Review

From Soma-notes
Revision as of 18:21, 13 March 2014 by Leah (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

The video from the lecture given on Feb. 12, 2014 is available:

The midterm will be based on small-adventure, a version of adventure-demo that has been simplified by removing the use of cryptography (https and bcrypt) along with a few other minor tweaks to reduce the size of the source code.

No new tutorial this week; however, two TAs/instructors will be in the lab on Thursday 4-5:30 and Friday 8:30-10 to answer last minute questions.

Midterm Review Notes

  • Most of the notes are written inline in the source code to make the line it refers to more clear:

What is the state of the system after app,js runs?

  • We don't know what it is because you only know when the call back is run.
  • app.get/app.post are only run ONCE, before the web server starts listening for incoming connections. It tells the web server what to do when http GET and POST requests are received at specific urls.

Asynchronous vs Synchronous:

  • The analogy that Anil gave was that synchronous is like a 5 year old child waiting for cookies to be baked. He asks "is it ready yet?" every 5 minutes. He's not doing anything else. Asynchronous is like a 8 year old child, who does something else (e.g. read a book) while he waits for the cookies to be baked

Source for small-adventure

app.js

var express = require('express'); //requires the express module
var routes = require('./routes'); //same as ('/routes/index.js'). The require() method returns the exports object
var path = require('path'); //imports the path module, or path.join will not be defined
var http = require('http'); //requires the http module
var MongoStore = require('connect-mongo')(express);

var app = express(); //creates an express object instance and assigns the app variable to it.

app.set('port', process.env.PORT || 3000); //listens on PORT environment variable OR port 3000 if not defined
app.set('views', path.join(__dirname, 'views')); //sets the location for application views
app.set('view engine', 'jade'); //sets the view engine, Jade
app.use(express.favicon()); //can specify where you want favicon to be found in the parameters. e.g (_dirname + 'public/favicon.ico')
app.use(express.logger('dev')); //logging requests using predefined log format 'dev'. This will generate a detail log.
app.use(express.urlencoded()); //The body of HTTP post requests. Parses the incoming request body, converting it into request object properties

app.use(express.cookieParser()); 
app.use(express.session({
    cookie: {maxAge: 60000 * 20} // 20 minutes
    , secret: "Shh... I'm a secret" //This is the secret that prevents attackers from creating their own fake session identifiers
    , store: new MongoStore({db: "adventure-demo"})
}));

app.use(app.router);
app.use(express.static(path.join(__dirname, 'public'))); //serves static files from the root path. The CSS files and fonts are all stored in the public directory, which is made publicly accessible by this line.

if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

//The purpose of these lines is to setup routes. It tells the web server what to do when http GET and POST requests are received at specific urls. Note that these are only run ONCE, before the web server starts listening for incoming connections.
app.get('/', routes.redirectLoggedIn, routes.index); //requests the home page (https://localhost:3000)
app.post("/register", routes.register);
app.post("/start", routes.start);
app.post("/quit", routes.quit);

var createRooms = function() {
    var i, theRoom;

    routes.getRooms().toArray( //a call to getRooms and a call toArray, which is a method of the collections object. This runs the query and puts the results into an array
        function(err, docs) {
            if (err) {
                throw "Couldn't find active room list";
            }
            
            var activeRooms = docs[0].activeRooms;

            activeRooms.forEach(function(roomName) { //roomName is our index variable, we get the room element. ActiveRooms is an array of roomName.
                console.log('Creating room: ' + roomName);
                app.get('/' + roomName,
                        routes.makeRoomHandler(roomName));
            });
        }
    );
}
routes.connectToDBs(createRooms);

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port') +
              ' in ' + app.get('env') + ' mode.');
});
//1) createServer(): a method of http that creates and returns a server object
//2) listen(): a method at the returned server object that starts the web server listening on a given port
//3) app.get(): returns the port to listen on


routes/index.js

var mc = require('mongodb').MongoClient;
var playersCollection, roomsCollection;
exports.connectToDBs = function(callback) {
    mc.connect('mongodb://localhost/adventure-demo', function(err, db) {
        if (err) {
            throw err;
        }
        
        playersCollection = db.collection('players');
        roomsCollection = db.collection('rooms');

        callback(); //is createRooms. We do the callback after the database is established because we need to setup the database first.
    });
}

//assigned to the properties of the exports object
exports.index = function(req, res){
    res.render('index', { title: 'COMP 2406 Small Adventure Demo',
                          error: req.query.error });
}
exports.redirectLoggedIn = function(req, res, next) {
    if (req.session.player) {
        res.redirect("/" + req.session.player.room);
    } else {
        next();
    }
}
exports.redirectNotLoggedIn = function(req, res, next) {
    if (req.session.player) {
        next();
    } else {
        res.redirect("/");
    }
}
exports.register = function(req, res) {
    var playername = req.body.playername; //req.body retrieves the value for the playername attribute
    var password = req.body.password;

    var addPlayer = function(err, players) {
        if(players.length!=0){
            res.redirect("/?error=player already exists");      
            return;
        }
        
        var newPlayer = {
            playername: playername,
            password: password,
            room: "bridge"
        };
                
        playersCollection.insert(newPlayer, function(err, newPlayers){
            if (err) {
                throw err;
            } else {
                res.send('Successfully registered ' + newPlayers[0].playername);
            }
        });    
    }
    
    playersCollection.find({playername: playername}).toArray(addPlayer);
}
exports.start = function(req, res){
    var playername = req.body.playername;
    var password = req.body.password;

    playersCollection.findOne({playername: playername}, function(err, player) {
        if (err || !player){
            req.session.destroy(function(err) {
                res.redirect("/?error=invalid playername or password"); 
            });
            return;
        }
        
        if (password === player.password) {
            req.session.player = player;
            delete req.session.player._id;
            res.redirect("/" + player.room);
        } else {
            req.session.destroy(function(err) {
                res.redirect("/?error=invalid playername or password");
            });
        }
    });
}
exports.quit = function(req, res){
    req.session.destroy(function(err){
        if(err){
            console.log("Error: %s", err);
        }
        res.redirect("/");
    }); 
}
exports.makeRoomHandler = function(roomName) {
    var handler = function(req, res) { //without 'var' handler becomes a global variable
        if (req.session.player) {
            var player = req.session.player;
            player.room = roomName;
            playersCollection.update({"playername": player.playername},
                                     player, function(err, count) {
                                         if (err) {
                                             console.log(
                                                 "Couldn't save player state");
                                         }
                                     });
            roomsCollection.findOne(
                {name: roomName},
                function(err, room) {
                    if (err || !room) {
                        throw "Couldn't find room " + roomName;
                    }
                    res.render("room.jade", room); //renders an html page with room.jade template
                }
            );
        } else {
            res.redirect("/");
        }
    }
    return handler;
}
exports.getRooms = function() {
    return roomsCollection.find({name: "roomList"}); //finds all the rooms with the name in the roomlist
}

public/javascripts/home.js

$(function(){
	$("#register").on("click",function(){
		var $form = $("form");
		$form.attr("action","/register");
		$form.submit();
	});
});


views/layout.jade

doctype html
html
  head
    title= title
    script(src='/vendor/jquery/jquery.js')
    script('vendor/bootstrap/dist/js/bootstrap.js')
    link(rel='stylesheet', href='/vendor/bootstrap/dist/css/bootstrap.css')
    link(rel='stylesheet', href='/stylesheets/style.css')
    block header  
  body
    block content


views/index.jade

extends layout //inherits formatting from layout.jade

block header
  script(src='/javascripts/home.js') //applies javascript. Affects the register button

block content
  h1= title
  - if(error)
    div.alert-error #{error}
  p Please log in
  div
    form(action="/start", method="post")
        div.control-group.input-append
            input(type="text", name="playername")
            label.add-on(for="playername") Player Name
        div.control-group.input-append
            input(type="password", name="password")
            label.add-on(for="password") Password
            
        button(type="submit") Start
        button#register(type="button") Register


views/room.jade

extends layout

block content
  h1= title
  p #{description}
  p Go to:
  ul
    each theExit in roomExits //exits are listed for every room
      li
        a(href= theExit) #{theExit}
  form(action="/quit", method="post")
     button(type="submit") Quit //creates a quit button on every room