From 7bddd337fe8f9ce57acd62947cdded7d4e07f253 Mon Sep 17 00:00:00 2001 From: Radek Stepan Date: Sun, 12 Oct 2014 17:30:18 -0700 Subject: [PATCH] stop saving rubbish on chart page --- TODO.md | 1 + public/js/app.bundle.js | 201 ++++++++++++++++++++++-------- public/js/app.js | 201 ++++++++++++++++++++++-------- src/models/firebase.coffee | 10 +- src/modules/github/request.coffee | 88 +++++++++---- src/templates/header.mustache | 4 +- src/utils/mixins.coffee | 5 +- src/views/pages/milestone.coffee | 14 ++- 8 files changed, 381 insertions(+), 143 deletions(-) diff --git a/TODO.md b/TODO.md index 24860f5..43a5a94 100644 --- a/TODO.md +++ b/TODO.md @@ -15,6 +15,7 @@ ###Error Handling +- [ ] second visit to a different milestone comes out blank - [ ] verify that project exists on project page when fetching it remotely (add behind the scenes) - [ ] deal with Firebase timing out, are we still logged-in? - [ ] visiting a chart page saves the project if it isn't saved already diff --git a/public/js/app.bundle.js b/public/js/app.bundle.js index b99e6f8..6ba4244 100644 --- a/public/js/app.bundle.js +++ b/public/js/app.bundle.js @@ -39141,17 +39141,16 @@ Router.prototype.mount = function(routes, path) { return user.reset(); }, onrender: function() { - var client, - _this = this; + var client; this.set('client', client = new Firebase("https://" + config.data.firebase + ".firebaseio.com")); return this.auth = new FirebaseSimpleLogin(client, function(err, obj) { - user.set('loaded', true); if (err) { throw err; } if (obj) { - return user.set(obj); + user.set(obj); } + return user.set('ready', true); }); } }); @@ -39360,7 +39359,7 @@ Router.prototype.mount = function(routes, path) { // request.coffee root.require.register('burnchart/src/modules/github/request.js', function(exports, require, module) { - var defaults, error, headers, request, response, user; + var defaults, error, headers, isReady, isValid, ready, request, response, stack, user; user = require('../../models/user'); @@ -39385,54 +39384,92 @@ Router.prototype.mount = function(routes, path) { module.exports = { repo: function(_arg, cb) { - var data, name, owner; + var name, owner; owner = _arg.owner, name = _arg.name; - data = _.defaults({ - 'path': "/repos/" + owner + "/" + name, - 'headers': headers(user.data.token) - }, defaults.github); - return request(data, cb); + if (!isValid({ + owner: owner, + name: name + })) { + return cb('Request is malformed'); + } + return ready(function() { + var data; + data = _.defaults({ + 'path': "/repos/" + owner + "/" + name, + 'headers': headers(user.data.accessToken) + }, defaults.github); + return request(data, cb); + }); }, allMilestones: function(_arg, cb) { - var data, name, owner; + var name, owner; owner = _arg.owner, name = _arg.name; - data = _.defaults({ - 'path': "/repos/" + owner + "/" + name + "/milestones", - 'query': { - 'state': 'open', - 'sort': 'due_date', - 'direction': 'asc' - }, - 'headers': headers(user.data.token) - }, defaults.github); - return request(data, cb); + if (!isValid({ + owner: owner, + name: name + })) { + return cb('Request is malformed'); + } + return ready(function() { + var data; + data = _.defaults({ + 'path': "/repos/" + owner + "/" + name + "/milestones", + 'query': { + 'state': 'open', + 'sort': 'due_date', + 'direction': 'asc' + }, + 'headers': headers(user.data.accessToken) + }, defaults.github); + return request(data, cb); + }); }, oneMilestone: function(_arg, cb) { - var data, milestone, name, owner; + var milestone, name, owner; owner = _arg.owner, name = _arg.name, milestone = _arg.milestone; - data = _.defaults({ - 'path': "/repos/" + owner + "/" + name + "/milestones/" + milestone, - 'query': { - 'state': 'open', - 'sort': 'due_date', - 'direction': 'asc' - }, - 'headers': headers(user.data.token) - }, defaults.github); - return request(data, cb); + if (!isValid({ + owner: owner, + name: name, + milestone: milestone + })) { + return cb('Request is malformed'); + } + return ready(function() { + var data; + data = _.defaults({ + 'path': "/repos/" + owner + "/" + name + "/milestones/" + milestone, + 'query': { + 'state': 'open', + 'sort': 'due_date', + 'direction': 'asc' + }, + 'headers': headers(user.data.accessToken) + }, defaults.github); + return request(data, cb); + }); }, allIssues: function(_arg, query, cb) { - var data, milestone, name, owner; + var milestone, name, owner; owner = _arg.owner, name = _arg.name, milestone = _arg.milestone; - data = _.defaults({ - 'path': "/repos/" + owner + "/" + name + "/issues", - 'query': _.extend(query, { - milestone: milestone, - 'per_page': '100' - }), - 'headers': headers(user.data.token) - }, defaults.github); - return request(data, cb); + if (!isValid({ + owner: owner, + name: name, + milestone: milestone + })) { + return cb('Request is malformed'); + } + return ready(function() { + var data; + data = _.defaults({ + 'path': "/repos/" + owner + "/" + name + "/issues", + 'query': _.extend(query, { + milestone: milestone, + 'per_page': '100' + }), + 'headers': headers(user.data.accessToken) + }, defaults.github); + return request(data, cb); + }); } }; @@ -39484,16 +39521,62 @@ Router.prototype.mount = function(routes, path) { headers = function(token) { var h; - h = _.extend({}, { + h = { 'Content-Type': 'application/json', 'Accept': 'application/vnd.github.v3' - }); + }; if (token != null) { h.Authorization = "token " + token; } return h; }; + isValid = function(obj) { + var key, rules, val; + rules = { + 'owner': function(val) { + return val != null; + }, + 'name': function(val) { + return val != null; + }, + 'milestone': function(val) { + return _.isInt(val); + } + }; + for (key in obj) { + val = obj[key]; + if (key in rules && !rules[key](val)) { + return false; + } + } + return true; + }; + + isReady = user.data.ready; + + stack = []; + + ready = function(cb) { + if (isReady) { + return cb(); + } else { + return stack.push(cb); + } + }; + + user.observe('ready', function(val) { + var _results; + isReady = val; + if (val) { + _results = []; + while (stack.length) { + _results.push(stack.shift()()); + } + return _results; + } + }); + error = function(err) { var message; switch (false) { @@ -39758,7 +39841,7 @@ Router.prototype.mount = function(routes, path) { // header.mustache root.require.register('burnchart/src/templates/header.js', function(exports, require, module) { - module.exports = ["
"," {{#with user}}"," {{#loaded}}","
"," {{#displayName}}"," {{displayName}} logged in"," {{else}}"," Sign In"," {{/displayName}}","
"," {{/loaded}}"," {{/with}}",""," "," "," ",""," ",""," ","
"].join("\n"); + module.exports = ["
"," {{#with user}}"," {{#ready}}","
"," {{#displayName}}"," {{displayName}} logged in"," {{else}}"," Sign In"," {{/displayName}}","
"," {{/ready}}"," {{/with}}",""," "," "," ",""," ",""," ","
"].join("\n"); }); // hero.mustache @@ -39907,6 +39990,9 @@ Router.prototype.mount = function(routes, path) { }); return obj; }); + }, + 'isInt': function(val) { + return !isNaN(val) && parseInt(Number(val)) === val && !isNaN(parseInt(val, 10)); } }); @@ -40290,6 +40376,7 @@ Router.prototype.mount = function(routes, path) { var done, fetchIssues, fetchMilestone, milestone, name, obj, owner, project, _ref, _this = this; _ref = this.get('route'), owner = _ref[0], name = _ref[1], milestone = _ref[2]; + milestone = parseInt(milestone); document.title = "" + owner + "/" + name + "/" + milestone; project = projects.find({ owner: owner, @@ -40309,13 +40396,19 @@ Router.prototype.mount = function(routes, path) { } done = system.async(); fetchMilestone = function(cb) { - return milestones.fetch(_.extend(project, { + return milestones.fetch({ + owner: owner, + name: name, milestone: milestone - }), cb); + }, cb); }; - fetchIssues = function(milestone, cb) { - return issues.fetchAll(project, function(err, obj) { - return cb(err, _.extend(milestone, { + fetchIssues = function(data, cb) { + return issues.fetchAll({ + owner: owner, + name: name, + milestone: milestone + }, function(err, obj) { + return cb(err, _.extend(data, { 'issues': obj })); }); @@ -40330,7 +40423,11 @@ Router.prototype.mount = function(routes, path) { 'ttl': null }); } - projects.push('list', data); + if (project.milestones == null) { + project.milestones = []; + } + project.milestones.push(data); + projects.update('list'); return _this.set({ 'milestone': data, 'ready': true diff --git a/public/js/app.js b/public/js/app.js index 13c77c8..0dcf797 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -93,17 +93,16 @@ return user.reset(); }, onrender: function() { - var client, - _this = this; + var client; this.set('client', client = new Firebase("https://" + config.data.firebase + ".firebaseio.com")); return this.auth = new FirebaseSimpleLogin(client, function(err, obj) { - user.set('loaded', true); if (err) { throw err; } if (obj) { - return user.set(obj); + user.set(obj); } + return user.set('ready', true); }); } }); @@ -312,7 +311,7 @@ // request.coffee root.require.register('burnchart/src/modules/github/request.js', function(exports, require, module) { - var defaults, error, headers, request, response, user; + var defaults, error, headers, isReady, isValid, ready, request, response, stack, user; user = require('../../models/user'); @@ -337,54 +336,92 @@ module.exports = { repo: function(_arg, cb) { - var data, name, owner; + var name, owner; owner = _arg.owner, name = _arg.name; - data = _.defaults({ - 'path': "/repos/" + owner + "/" + name, - 'headers': headers(user.data.token) - }, defaults.github); - return request(data, cb); + if (!isValid({ + owner: owner, + name: name + })) { + return cb('Request is malformed'); + } + return ready(function() { + var data; + data = _.defaults({ + 'path': "/repos/" + owner + "/" + name, + 'headers': headers(user.data.accessToken) + }, defaults.github); + return request(data, cb); + }); }, allMilestones: function(_arg, cb) { - var data, name, owner; + var name, owner; owner = _arg.owner, name = _arg.name; - data = _.defaults({ - 'path': "/repos/" + owner + "/" + name + "/milestones", - 'query': { - 'state': 'open', - 'sort': 'due_date', - 'direction': 'asc' - }, - 'headers': headers(user.data.token) - }, defaults.github); - return request(data, cb); + if (!isValid({ + owner: owner, + name: name + })) { + return cb('Request is malformed'); + } + return ready(function() { + var data; + data = _.defaults({ + 'path': "/repos/" + owner + "/" + name + "/milestones", + 'query': { + 'state': 'open', + 'sort': 'due_date', + 'direction': 'asc' + }, + 'headers': headers(user.data.accessToken) + }, defaults.github); + return request(data, cb); + }); }, oneMilestone: function(_arg, cb) { - var data, milestone, name, owner; + var milestone, name, owner; owner = _arg.owner, name = _arg.name, milestone = _arg.milestone; - data = _.defaults({ - 'path': "/repos/" + owner + "/" + name + "/milestones/" + milestone, - 'query': { - 'state': 'open', - 'sort': 'due_date', - 'direction': 'asc' - }, - 'headers': headers(user.data.token) - }, defaults.github); - return request(data, cb); + if (!isValid({ + owner: owner, + name: name, + milestone: milestone + })) { + return cb('Request is malformed'); + } + return ready(function() { + var data; + data = _.defaults({ + 'path': "/repos/" + owner + "/" + name + "/milestones/" + milestone, + 'query': { + 'state': 'open', + 'sort': 'due_date', + 'direction': 'asc' + }, + 'headers': headers(user.data.accessToken) + }, defaults.github); + return request(data, cb); + }); }, allIssues: function(_arg, query, cb) { - var data, milestone, name, owner; + var milestone, name, owner; owner = _arg.owner, name = _arg.name, milestone = _arg.milestone; - data = _.defaults({ - 'path': "/repos/" + owner + "/" + name + "/issues", - 'query': _.extend(query, { - milestone: milestone, - 'per_page': '100' - }), - 'headers': headers(user.data.token) - }, defaults.github); - return request(data, cb); + if (!isValid({ + owner: owner, + name: name, + milestone: milestone + })) { + return cb('Request is malformed'); + } + return ready(function() { + var data; + data = _.defaults({ + 'path': "/repos/" + owner + "/" + name + "/issues", + 'query': _.extend(query, { + milestone: milestone, + 'per_page': '100' + }), + 'headers': headers(user.data.accessToken) + }, defaults.github); + return request(data, cb); + }); } }; @@ -436,16 +473,62 @@ headers = function(token) { var h; - h = _.extend({}, { + h = { 'Content-Type': 'application/json', 'Accept': 'application/vnd.github.v3' - }); + }; if (token != null) { h.Authorization = "token " + token; } return h; }; + isValid = function(obj) { + var key, rules, val; + rules = { + 'owner': function(val) { + return val != null; + }, + 'name': function(val) { + return val != null; + }, + 'milestone': function(val) { + return _.isInt(val); + } + }; + for (key in obj) { + val = obj[key]; + if (key in rules && !rules[key](val)) { + return false; + } + } + return true; + }; + + isReady = user.data.ready; + + stack = []; + + ready = function(cb) { + if (isReady) { + return cb(); + } else { + return stack.push(cb); + } + }; + + user.observe('ready', function(val) { + var _results; + isReady = val; + if (val) { + _results = []; + while (stack.length) { + _results.push(stack.shift()()); + } + return _results; + } + }); + error = function(err) { var message; switch (false) { @@ -710,7 +793,7 @@ // header.mustache root.require.register('burnchart/src/templates/header.js', function(exports, require, module) { - module.exports = ["
"," {{#with user}}"," {{#loaded}}","
"," {{#displayName}}"," {{displayName}} logged in"," {{else}}"," Sign In"," {{/displayName}}","
"," {{/loaded}}"," {{/with}}",""," "," "," ",""," ",""," ","
"].join("\n"); + module.exports = ["
"," {{#with user}}"," {{#ready}}","
"," {{#displayName}}"," {{displayName}} logged in"," {{else}}"," Sign In"," {{/displayName}}","
"," {{/ready}}"," {{/with}}",""," "," "," ",""," ",""," ","
"].join("\n"); }); // hero.mustache @@ -859,6 +942,9 @@ }); return obj; }); + }, + 'isInt': function(val) { + return !isNaN(val) && parseInt(Number(val)) === val && !isNaN(parseInt(val, 10)); } }); @@ -1242,6 +1328,7 @@ var done, fetchIssues, fetchMilestone, milestone, name, obj, owner, project, _ref, _this = this; _ref = this.get('route'), owner = _ref[0], name = _ref[1], milestone = _ref[2]; + milestone = parseInt(milestone); document.title = "" + owner + "/" + name + "/" + milestone; project = projects.find({ owner: owner, @@ -1261,13 +1348,19 @@ } done = system.async(); fetchMilestone = function(cb) { - return milestones.fetch(_.extend(project, { + return milestones.fetch({ + owner: owner, + name: name, milestone: milestone - }), cb); + }, cb); }; - fetchIssues = function(milestone, cb) { - return issues.fetchAll(project, function(err, obj) { - return cb(err, _.extend(milestone, { + fetchIssues = function(data, cb) { + return issues.fetchAll({ + owner: owner, + name: name, + milestone: milestone + }, function(err, obj) { + return cb(err, _.extend(data, { 'issues': obj })); }); @@ -1282,7 +1375,11 @@ 'ttl': null }); } - projects.push('list', data); + if (project.milestones == null) { + project.milestones = []; + } + project.milestones.push(data); + projects.update('list'); return _this.set({ 'milestone': data, 'ready': true diff --git a/src/models/firebase.coffee b/src/models/firebase.coffee index 11b15a8..1983ca4 100644 --- a/src/models/firebase.coffee +++ b/src/models/firebase.coffee @@ -26,10 +26,10 @@ module.exports = new Model @set 'client', client = new Firebase "https://#{config.data.firebase}.firebaseio.com" # Check if we have a user in session. - @auth = new FirebaseSimpleLogin client, (err, obj) => - user.set 'loaded', yes - + @auth = new FirebaseSimpleLogin client, (err, obj) -> throw err if err - + # Save user. - user.set obj if obj \ No newline at end of file + user.set obj if obj + # Say we are done. + user.set 'ready', yes \ No newline at end of file diff --git a/src/modules/github/request.coffee b/src/modules/github/request.coffee index 8c46fbf..309c9a7 100644 --- a/src/modules/github/request.coffee +++ b/src/modules/github/request.coffee @@ -19,42 +19,54 @@ module.exports = # Get a repo. repo: ({ owner, name }, cb) -> - data = _.defaults - 'path': "/repos/#{owner}/#{name}" - 'headers': headers user.data.token - , defaults.github + return cb 'Request is malformed' unless isValid { owner, name } - request data, cb + ready -> + data = _.defaults + 'path': "/repos/#{owner}/#{name}" + 'headers': headers user.data.accessToken + , defaults.github + + request data, cb # Get all open milestones. - allMilestones: ({ owner, name }, cb) -> - data = _.defaults - 'path': "/repos/#{owner}/#{name}/milestones" - 'query': { 'state': 'open', 'sort': 'due_date', 'direction': 'asc' } - 'headers': headers user.data.token - , defaults.github + allMilestones: ({ owner, name }, cb) -> + return cb 'Request is malformed' unless isValid { owner, name } - request data, cb + ready -> + data = _.defaults + 'path': "/repos/#{owner}/#{name}/milestones" + 'query': { 'state': 'open', 'sort': 'due_date', 'direction': 'asc' } + 'headers': headers user.data.accessToken + , defaults.github + + request data, cb # Get one open milestone. oneMilestone: ({ owner, name, milestone }, cb) -> - data = _.defaults - 'path': "/repos/#{owner}/#{name}/milestones/#{milestone}" - 'query': { 'state': 'open', 'sort': 'due_date', 'direction': 'asc' } - 'headers': headers user.data.token - , defaults.github + return cb 'Request is malformed' unless isValid { owner, name, milestone } - request data, cb + ready -> + data = _.defaults + 'path': "/repos/#{owner}/#{name}/milestones/#{milestone}" + 'query': { 'state': 'open', 'sort': 'due_date', 'direction': 'asc' } + 'headers': headers user.data.accessToken + , defaults.github + + request data, cb # Get all issues for a state. - allIssues: ({ owner, name, milestone }, query, cb) -> - data = _.defaults - 'path': "/repos/#{owner}/#{name}/issues" - 'query': _.extend query, { milestone, 'per_page': '100' } - 'headers': headers user.data.token - , defaults.github + allIssues: ({ owner, name, milestone }, query, cb) -> + return cb 'Request is malformed' unless isValid { owner, name, milestone } - request data, cb + ready -> + data = _.defaults + 'path': "/repos/#{owner}/#{name}/issues" + 'query': _.extend query, { milestone, 'per_page': '100' } + 'headers': headers user.data.accessToken + , defaults.github + + request data, cb # Make a request using SuperAgent. request = ({ protocol, host, path, query, headers }, cb) -> @@ -99,13 +111,37 @@ response = (err, data, cb) -> # Give us headers. headers = (token) -> # The defaults. - h = _.extend {}, + h = 'Content-Type': 'application/json' 'Accept': 'application/vnd.github.v3' # Add token? h.Authorization = "token #{token}" if token? h +isValid = (obj) -> + rules = + 'owner': (val) -> val? + 'name': (val) -> val? + 'milestone': (val) -> _.isInt val + + ( return no for key, val of obj when key of rules and not rules[key](val) ) + + yes + +# Switch when user is ready. +isReady = user.data.ready + +# A stack of requests to execute once ready. +stack = [] +ready = (cb) -> + if isReady then do cb else stack.push cb + +# Observe user's readiness. +user.observe 'ready', (val) -> + isReady = val + # Clear the stack? + ( do stack.shift() while stack.length ) if val + # Parse an error. error = (err) -> switch diff --git a/src/templates/header.mustache b/src/templates/header.mustache index 1a99d1a..0e9eeef 100644 --- a/src/templates/header.mustache +++ b/src/templates/header.mustache @@ -1,6 +1,6 @@