From 7a8e4707a3078578cf3ae0fb0413dbb8c01343cc Mon Sep 17 00:00:00 2001 From: imakewebthings Date: Tue, 10 Dec 2013 21:50:33 -0800 Subject: [PATCH] Way too much for one commit hashtag yolo --- .gitignore | 1 + Procfile | 1 + app/chatList.js | 4 +++- app/chatStream.js | 13 +++++++++++++ app/matchmaker.js | 35 +++++++++++++++++++++++++++++++++ app/routes.js | 19 ++++++++++++++++++ app/settings.js | 17 ++++++++++++++++ config.sample.json | 3 +++ index.js | 13 +++++++++++++ package.json | 19 ++++++++++++++---- test/chatList.js | 34 ++++++++++++++++++++++---------- test/chatStream.js | 29 ++++++++++++++++++++++++++++ test/matchmaker.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++ views/index.jade | 12 ++++++++++++ views/layout.jade | 12 ++++++++++++ views/results.jade | 13 +++++++++++++ views/warming.jade | 4 ++++ 17 files changed, 262 insertions(+), 15 deletions(-) create mode 100644 Procfile create mode 100644 app/chatStream.js create mode 100644 app/matchmaker.js create mode 100644 app/routes.js create mode 100644 app/settings.js create mode 100644 config.sample.json create mode 100644 index.js create mode 100644 test/chatStream.js create mode 100644 test/matchmaker.js create mode 100644 views/index.jade create mode 100644 views/layout.jade create mode 100644 views/results.jade create mode 100644 views/warming.jade diff --git a/.gitignore b/.gitignore index 1bd7226..39cdfae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules *.swp +config.json diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..1da0cd6 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: node index.js diff --git a/app/chatList.js b/app/chatList.js index b6c8fb8..b13d3f3 100644 --- a/app/chatList.js +++ b/app/chatList.js @@ -3,7 +3,9 @@ var ChatList = function() { }; ChatList.prototype.push = function(chat) { - this.chats.push(chat); + if (chat.value.message) { + this.chats.push(chat); + } if (this.chats.length > 200) { this.chats.shift(); } diff --git a/app/chatStream.js b/app/chatStream.js new file mode 100644 index 0000000..7dd9342 --- /dev/null +++ b/app/chatStream.js @@ -0,0 +1,13 @@ +var socketClient = require('socket.io-client'); + +var ChatStream = function(chatList, nconf) { + this.chatList = chatList; + this.socket = socketClient.connect(nconf.get('SOCKET_URL')); + this.socket.on('message', this.handleMessage.bind(this)); +}; + +ChatStream.prototype.handleMessage = function(data) { + this.chatList.push(data.chat); +}; + +module.exports = ChatStream; diff --git a/app/matchmaker.js b/app/matchmaker.js new file mode 100644 index 0000000..040c192 --- /dev/null +++ b/app/matchmaker.js @@ -0,0 +1,35 @@ +var shuffle = require('knuth-shuffle').knuthShuffle; + +var Matchmaker = function(chatList) { + this.chatList = chatList; +}; + +Matchmaker.prototype.generate = function() { + if (this.chatList.chats.length < 30) { + return null; + } + var randomIndices = []; + var match = { + answers: [] + }; + + while(randomIndices.length < 5) { + var num = Math.floor(Math.random() * this.chatList.chats.length); + if (randomIndices.indexOf(num) === -1) { + randomIndices.push(num); + } + } + randomIndices.forEach(function(chatListIndex, randIndex) { + var chat = this.chatList.chats[chatListIndex]; + match.answers.push(chat.value.message); + if (!randIndex) { + match.rightAnswer = chat.value.message; + match.gif = chat.value.media; + } + }.bind(this)); + + match.answers = shuffle(match.answers.slice(0)); + return match; +}; + +module.exports = Matchmaker; diff --git a/app/routes.js b/app/routes.js new file mode 100644 index 0000000..0313124 --- /dev/null +++ b/app/routes.js @@ -0,0 +1,19 @@ +module.exports = function(app, matchmaker) { + app.get('/', function(req, res) { + var match = matchmaker.generate(); + if (match) { + req.session.match = match; + res.render('index', { match: match }); + } + else { + res.render('warming'); + } + }); + + app.post('/', function(req, res) { + res.render('results', { + userAnswer: req.body.answer, + match: req.session.match + }); + }); +}; diff --git a/app/settings.js b/app/settings.js new file mode 100644 index 0000000..782ee3b --- /dev/null +++ b/app/settings.js @@ -0,0 +1,17 @@ +var express = require('express'); +var path = require('path'); + +module.exports = function(app, nconf) { + app.set('views', path.resolve(__dirname, '..', 'views')); + app.set('view engine', 'jade'); + app.set('view options', { layout: false }); + app.use(express.static(__dirname + '/public')); + app.use(express.bodyParser()); + app.use(express.cookieParser()); + app.use(express.session({ + secret: nconf.get('COOKIE_SECRET'), + key: 'meatsess', + store: new express.session.MemoryStore() + })); + app.use(app.router); +}; diff --git a/config.sample.json b/config.sample.json new file mode 100644 index 0000000..deca16b --- /dev/null +++ b/config.sample.json @@ -0,0 +1,3 @@ +{ + "PORT": 5000 +} diff --git a/index.js b/index.js new file mode 100644 index 0000000..2a52885 --- /dev/null +++ b/index.js @@ -0,0 +1,13 @@ +var express = require('express'); +var app = express(); +var nconf = require('nconf').argv().env().file('config.json'); +var ChatList = require('./app/chatList'); +var ChatStream = require('./app/chatStream'); +var Matchmaker = require('./app/matchmaker'); +var chatList = new ChatList(); +var chatStream = new ChatStream(chatList, nconf); +var matchmaker = new Matchmaker(chatList); + +require('./app/settings')(app, nconf); +require('./app/routes')(app, matchmaker); +app.listen(nconf.get('PORT')); diff --git a/package.json b/package.json index 23b64f7..f646eb7 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,10 @@ "version": "0.0.0", "description": "Matching meat gifs to messagses", "main": "index.js", + "private": true, "scripts": { - "tdd": "testem" + "tdd": "testem", + "start": "node index.js" }, "repository": { "type": "git", @@ -12,16 +14,25 @@ }, "keywords": [ "fun", - "stuff" + "meatspaces" ], "author": "cool dudes", - "license": "BSD-2-Clause", + "license": "MIT", "bugs": { "url": "https://github.com/kid-icarus/Meatmatch/issues" }, "devDependencies": { "testem": "~0.5.12", "chai": "~1.8.1", - "mocha": "~1.15.1" + "mocha": "~1.15.1", + "socket.io": "~0.9.16" + }, + "dependencies": { + "express": "~3.4.6", + "nconf": "~0.6.9", + "socket.io-client": "~0.9.16", + "jade": "~0.35.0", + "connect": "~2.11.2", + "knuth-shuffle": "~1.0.0" } } diff --git a/test/chatList.js b/test/chatList.js index 4bbc4cf..ee0a4cc 100644 --- a/test/chatList.js +++ b/test/chatList.js @@ -1,6 +1,16 @@ var ChatList = require('../app/chatList'); var should = require('chai').should(); +var mockChat = function(message) { + return { + key: 'lorem', + value: { + message: message == null ? 'ipsum' : message, + media: 'dolor' + } + }; +}; + describe('Chat List', function() { beforeEach(function() { this.chatList = new ChatList(); @@ -8,40 +18,44 @@ describe('Chat List', function() { describe('#push', function() { beforeEach(function() { - this.chatList.push({'first': true}); + var chat = mockChat(); + chat.first = true; + this.chatList.push(chat); }); - it('should push a message into the chats array', function() { + it('pushes a message into the chats array', function() { this.chatList.chats.should.have.length(1); }); - it('should pop the oldest chat off after 200 chats', function() { + it('pops the oldest chat off after 200 chats', function() { for(var i = 0; i < 200; i++) { - this.chatList.push({}); + this.chatList.push(mockChat()); } this.chatList.chats[0].should.not.have.property('first'); }); + it('rejects chats with blank messages', function() { + this.chatList.push(mockChat('')); + this.chatList.pop().should.have.property('first'); + }); }); describe('#pop', function() { beforeEach(function() { - this.chatList.push({}); + this.chatList.push(mockChat()); this.chat = this.chatList.pop(); }); - it('should return an object', function(){ + it('returns a chat object', function(){ this.chat.should.be.an('object'); }); - it('should pop off the chats array', function() { + it('pops off the chats array', function() { this.chatList.chats.should.have.length(0); }); - it('should return undefined if chatList is empty', function() { + it('returns undefined if chatList is empty', function() { should.not.exist(this.chatList.pop()); }); - }); }); - diff --git a/test/chatStream.js b/test/chatStream.js new file mode 100644 index 0000000..96f5f27 --- /dev/null +++ b/test/chatStream.js @@ -0,0 +1,29 @@ +var ChatStream = require('../app/chatStream'); +var should = require('chai').should(); +var io = require('socket.io').listen(8080, { log: false }); +var nconf = require('nconf'); +var ChatList = require('../app/chatList'); + +nconf.defaults({ + SOCKET_URL: 'http://localhost:8080' +}); + +describe('Chat Streamer', function() { + before(function(done) { + io.sockets.on('connection', function(socket) { + this.socket = socket; + done(); + }.bind(this)); + this.chatList = new ChatList(); + this.chatStream = new ChatStream(this.chatList, nconf); + }); + + it('pushes incoming messages to a ChatList', function(done) { + this.chatStream.socket.on('message', function() { + this.chatList.pop().should.exist; + done(); + }.bind(this)); + this.socket.emit('message', { chat: { value: { message: 'test' }}}); + }); +}); + diff --git a/test/matchmaker.js b/test/matchmaker.js new file mode 100644 index 0000000..b830ef1 --- /dev/null +++ b/test/matchmaker.js @@ -0,0 +1,48 @@ +var should = require('chai').should(); +var Matchmaker = require('../app/matchmaker'); +var ChatList = require('../app/chatList'); + +var fillChatList = function(chatList, n) { + for(var i = 0; i < n; i++) { + chatList.push({ + key: 'key-' + i, + value: { + message: 'message-' + i, + media: 'media-' + i + } + }); + } +}; + +describe('Matchmaker', function() { + beforeEach(function() { + this.chatList = new ChatList(); + this.matchmaker = new Matchmaker(this.chatList); + }); + + describe('#generate', function() { + describe('without minimum number of chats', function() { + beforeEach(function() { + fillChatList(this.chatList, 29); + this.match = this.matchmaker.generate(); + }); + + it('returns undefined', function() { + should.equal(this.match, null); + }); + }); + + describe('with a minimum number of chats', function() { + beforeEach(function() { + fillChatList(this.chatList, 30); + this.match = this.matchmaker.generate(); + }); + + it('returns a match object', function() { + this.match.gif.should.exist; + this.match.rightAnswer.should.exist; + this.match.answers.should.be.an('array'); + }); + }); + }); +}); diff --git a/views/index.jade b/views/index.jade new file mode 100644 index 0000000..1f831de --- /dev/null +++ b/views/index.jade @@ -0,0 +1,12 @@ +extend layout + +block content + img(src=match.gif, alt='') + form(method='post', action='/') + ul.answers + each answer in match.answers + li + label + input(type='radio', name='answer', value=answer) + = answer + input(type='submit', value='Final Answer') diff --git a/views/layout.jade b/views/layout.jade new file mode 100644 index 0000000..81efc76 --- /dev/null +++ b/views/layout.jade @@ -0,0 +1,12 @@ +!!! +html + head + title MeatMatch + meta(charset='utf8') + meta(name='viewport', content='width=device-width, initial-scale=1.0') + meta(name='apple-mobile-web-app-capable', content='yes') + link(rel='stylesheet', href='/stylesheets/main.css') + body + .page-wrapper + h1 MeatMatch + block content diff --git a/views/results.jade b/views/results.jade new file mode 100644 index 0000000..655461c --- /dev/null +++ b/views/results.jade @@ -0,0 +1,13 @@ +extend layout + +block content + - var answerCorrect = userAnswer === match.rightAnswer; + img(src=match.gif, alt='') + ul.answers + each answer in match.answers + - var classes = [] + - if (answer === userAnswer) classes.push('chosen') + - if (answer === match.rightAnswer) classes.push('correct') + li(class=classes.join(' '))= answer + h2= answerCorrect ? 'Correct' : 'Wrong' + a.more-meat(href='/') More Meat diff --git a/views/warming.jade b/views/warming.jade new file mode 100644 index 0000000..3fbed2e --- /dev/null +++ b/views/warming.jade @@ -0,0 +1,4 @@ +extend layout + +block content + p.warming-message The meat is warming up. Check back in a few.