First cut at some of the basics for Hubboard.

There's really too much to enumerate here, we're OAuthing a user and then
redirecting them onto the root page which auto-loads some fancy issues assigned
to said user.
This commit is contained in:
R. Tyler Croy 2012-03-18 03:23:24 -07:00
parent dede34a602
commit d75e6bc77f
16 changed files with 951 additions and 0 deletions

12
Gemfile Normal file
View File

@ -0,0 +1,12 @@
source :gemcutter
gem 'rake'
gem 'resin', '~> 0.0.6'
gem 'httparty'
gem 'i18n'
group :development do
gem 'rspec'
gem 'rack-test'
end

0
config/.gitkeep Normal file
View File

53
css/hub.css Normal file
View File

@ -0,0 +1,53 @@
body {
background: #e7e7e7;
background: url('/images/bg.png');
font-family: helvetica, sans-serif;
}
.issuecolumn {
width: 30%;
height: 100%;
float: left;
border-right: 1px solid #333;
border-bottom: 1px solid #333;
}
.issuecolumn .header {
text-align: center;
font-weight: bold;
background: #333;
margin-left: 5px;
margin-right: 5px;
color: #e7e7e7;
}
.issuetile {
margin: 10px;
padding: 5px;
border: 2px solid #333;
font-weight: bold;
background: #e7e7e7;
}
.issuetile .title {
font-size: 0.8em;
}
.issuetile .number {
font-size: 0.7em;
padding: 4px;
}
.issuetile .number .open {
}
.issuetile .inprogress {
background: #bbefbe;
}
.issuetile .number .closed {
}
.issuetile .number a:link, .issuetile .number a:visited {
color: inherit;
}

53
hubboard.rb Normal file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env ruby
require 'rubygems'
require 'cgi'
require 'httparty'
require 'resin/app/app'
$config = YAML::load(File.open(File.expand_path(File.dirname(__FILE__) + '/config/config.yml')))
module Hubboard
class Server < Resin::Server
set :sessions, true
get '/oauth' do
unless params.has_key? 'code'
halt 401, 'Failed to receive a valid OAuth code from GitHub'
end
response = HTTParty.post('https://github.com/login/oauth/access_token',
:query => {:client_id => $config['github']['id'],
:code => params['code'],
:client_secret => $config['github']['secret']})
unless response.code == 200
halt 418, 'We somehow got an invalid response from GitHub when trying to authenticate'
end
returned = CGI.parse(response.body)
access_token = returned["access_token"]
unless access_token
halt 401, 'Failed to pull an access token :('
end
session[:access_token] = access_token.first
redirect '/'
end
get '/' do
token = session[:access_token]
if token.nil? or token.empty?
redirect '/login'
end
puts "token: #{token}"
haml :index, :locals => {:access_token => token}
end
end
end
if $0 == __FILE__
Hubboard::Server.run!
end

BIN
images/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

67
js/GitHub.deploy.js Normal file
View File

@ -0,0 +1,67 @@
smalltalk.addPackage('GitHub', {});
smalltalk.addClass('Issues', smalltalk.Object, ['token', 'authenticated'], 'GitHub');
smalltalk.addMethod(
unescape('_baseUrl'),
smalltalk.method({
selector: unescape('baseUrl'),
fn: function (){
var self=this;
return unescape("https%3A//api.github.com");
return self;}
}),
smalltalk.Issues);
smalltalk.addMethod(
unescape('_setToken_'),
smalltalk.method({
selector: unescape('setToken%3A'),
fn: function (anAccessToken){
var self=this;
(self['@token']=anAccessToken);
(self['@authenticated']=true);
return self;}
}),
smalltalk.Issues);
smalltalk.addMethod(
unescape('_apiUrlFor_'),
smalltalk.method({
selector: unescape('apiUrlFor%3A'),
fn: function (apiString){
var self=this;
var url=nil;
(url=smalltalk.send(smalltalk.send("", "__comma", [smalltalk.send(self, "_baseUrl", [])]), "__comma", [apiString]));
((($receiver = self['@authenticated']).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (url=smalltalk.send(smalltalk.send(smalltalk.send("", "__comma", [url]), "__comma", [unescape("%3Faccess_token%3D")]), "__comma", [self['@token']]));})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return (url=smalltalk.send(smalltalk.send(smalltalk.send("", "__comma", [url]), "__comma", [unescape("%3Faccess_token%3D")]), "__comma", [self['@token']]));})]));
return url;
return self;}
}),
smalltalk.Issues);
smalltalk.addMethod(
unescape('_initialize'),
smalltalk.method({
selector: unescape('initialize'),
fn: function (){
var self=this;
(self['@authenticated']=false);
(self['@token']=nil);
return self;}
}),
smalltalk.Issues);
smalltalk.addMethod(
unescape('_issues_loadAll_'),
smalltalk.method({
selector: unescape('issues%3AloadAll%3A'),
fn: function (aPageCallback, shouldLoadAll){
var self=this;
try{((($receiver = self['@authenticated']).klass === smalltalk.Boolean) ? (! $receiver ? (function(){smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", ["Cannot load issues unless we have an access token"]);return (function(){throw({name: 'stReturn', selector: '_issues_loadAll_', fn: function(){return false}})})();})() : nil) : smalltalk.send($receiver, "_ifFalse_", [(function(){smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", ["Cannot load issues unless we have an access token"]);return (function(){throw({name: 'stReturn', selector: '_issues_loadAll_', fn: function(){return false}})})();})]));
smalltalk.send((typeof jQuery == 'undefined' ? nil : jQuery), "_ajax_options_", [smalltalk.send(self, "_apiUrlFor_", [unescape("/issues")]), smalltalk.HashedCollection._fromPairs_([smalltalk.send("dataType", "__minus_gt", ["jsonp"]),smalltalk.send("success", "__minus_gt", [(function(result){smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", [smalltalk.send(result, "_meta", [])]);smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", [smalltalk.send(result, "_data", [])]);return smalltalk.send(aPageCallback, "_value_", [smalltalk.send(result, "_data", [])]);})])])]);
(function(){throw({name: 'stReturn', selector: '_issues_loadAll_', fn: function(){return true}})})();
return self;
} catch(e) {if(e.name === 'stReturn' && e.selector === '_issues_loadAll_'){return e.fn()} throw(e)}}
}),
smalltalk.Issues);

92
js/GitHub.js Normal file
View File

@ -0,0 +1,92 @@
smalltalk.addPackage('GitHub', {});
smalltalk.addClass('Issues', smalltalk.Object, ['token', 'authenticated'], 'GitHub');
smalltalk.addMethod(
unescape('_baseUrl'),
smalltalk.method({
selector: unescape('baseUrl'),
category: 'not yet classified',
fn: function (){
var self=this;
return unescape("https%3A//api.github.com");
return self;},
args: [],
source: unescape('baseUrl%0A%09%22Return%20the%20GitHub%20API%20url%20base%22%0A%09%5E%20%27https%3A//api.github.com%27.'),
messageSends: [],
referencedClasses: []
}),
smalltalk.Issues);
smalltalk.addMethod(
unescape('_setToken_'),
smalltalk.method({
selector: unescape('setToken%3A'),
category: 'not yet classified',
fn: function (anAccessToken){
var self=this;
(self['@token']=anAccessToken);
(self['@authenticated']=true);
return self;},
args: ["anAccessToken"],
source: unescape('setToken%3A%20anAccessToken%0A%09%22Set%20the%20OAuth%20token%20we%20can%20use%22%0A%09token%20%3A%3D%20anAccessToken.%0A%09authenticated%20%3A%3D%20true.'),
messageSends: [],
referencedClasses: []
}),
smalltalk.Issues);
smalltalk.addMethod(
unescape('_apiUrlFor_'),
smalltalk.method({
selector: unescape('apiUrlFor%3A'),
category: 'not yet classified',
fn: function (apiString){
var self=this;
var url=nil;
(url=smalltalk.send(smalltalk.send("", "__comma", [smalltalk.send(self, "_baseUrl", [])]), "__comma", [apiString]));
((($receiver = self['@authenticated']).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (url=smalltalk.send(smalltalk.send(smalltalk.send("", "__comma", [url]), "__comma", [unescape("%3Faccess_token%3D")]), "__comma", [self['@token']]));})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return (url=smalltalk.send(smalltalk.send(smalltalk.send("", "__comma", [url]), "__comma", [unescape("%3Faccess_token%3D")]), "__comma", [self['@token']]));})]));
return url;
return self;},
args: ["apiString"],
source: unescape('apiUrlFor%3A%20apiString%0A%09%22Return%20the%20combined%20URL%20with%20an%20access%20token%20if%20we%20have%20one%22%0A%09%7C%20url%20%7C%0A%09url%20%3A%3D%20%27%27%2C%20%28self%20baseUrl%29%2C%20apiString.%0A%09authenticated%20ifTrue%3A%20%5B%20url%20%3A%3D%20%27%27%2C%20url%2C%20%27%3Faccess_token%3D%27%2C%20token%20%5D.%0A%09%5E%20url.'),
messageSends: [unescape("%2C"), "baseUrl", "ifTrue:"],
referencedClasses: []
}),
smalltalk.Issues);
smalltalk.addMethod(
unescape('_initialize'),
smalltalk.method({
selector: unescape('initialize'),
category: 'not yet classified',
fn: function (){
var self=this;
(self['@authenticated']=false);
(self['@token']=nil);
return self;},
args: [],
source: unescape('initialize%0A%09authenticated%20%3A%3D%20false.%0A%09token%20%3A%3D%20nil.'),
messageSends: [],
referencedClasses: []
}),
smalltalk.Issues);
smalltalk.addMethod(
unescape('_issues_loadAll_'),
smalltalk.method({
selector: unescape('issues%3AloadAll%3A'),
category: 'not yet classified',
fn: function (aPageCallback, shouldLoadAll){
var self=this;
try{((($receiver = self['@authenticated']).klass === smalltalk.Boolean) ? (! $receiver ? (function(){smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", ["Cannot load issues unless we have an access token"]);return (function(){throw({name: 'stReturn', selector: '_issues_loadAll_', fn: function(){return false}})})();})() : nil) : smalltalk.send($receiver, "_ifFalse_", [(function(){smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", ["Cannot load issues unless we have an access token"]);return (function(){throw({name: 'stReturn', selector: '_issues_loadAll_', fn: function(){return false}})})();})]));
smalltalk.send((typeof jQuery == 'undefined' ? nil : jQuery), "_ajax_options_", [smalltalk.send(self, "_apiUrlFor_", [unescape("/issues")]), smalltalk.HashedCollection._fromPairs_([smalltalk.send("dataType", "__minus_gt", ["jsonp"]),smalltalk.send("success", "__minus_gt", [(function(result){smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", [smalltalk.send(result, "_meta", [])]);smalltalk.send((typeof console == 'undefined' ? nil : console), "_log_", [smalltalk.send(result, "_data", [])]);return smalltalk.send(aPageCallback, "_value_", [smalltalk.send(result, "_data", [])]);})])])]);
(function(){throw({name: 'stReturn', selector: '_issues_loadAll_', fn: function(){return true}})})();
return self;
} catch(e) {if(e.name === 'stReturn' && e.selector === '_issues_loadAll_'){return e.fn()} throw(e)}},
args: ["aPageCallback", "shouldLoadAll"],
source: unescape('issues%3A%20aPageCallback%20loadAll%3A%20shouldLoadAll%0A%09%22Call%20aPageCallback%20with%20every%20page%20of%20issues%20loaded%20from%20GitHub%22%0A%09authenticated%20ifFalse%3A%20%5B%20console%20log%3A%20%27Cannot%20load%20issues%20unless%20we%20have%20an%20access%20token%27.%20%5E%20false%20%5D.%0A%0A%09jQuery%20ajax%3A%20%28self%20apiUrlFor%3A%20%27/issues%27%29%20options%3A%20%23%7B%0A%09%09%09%27dataType%27%20-%3E%20%27jsonp%27.%0A%09%09%09%27success%27%20-%3E%20%5B%20%3Aresult%20%7C%0A%09%09%09%09console%20log%3A%20%28result%20meta%29.%0A%09%09%09%09console%20log%3A%20%28result%20data%29.%0A%09%09%09%09aPageCallback%20value%3A%20%28result%20data%29.%0A%09%09%09%5D%0A%09%7D.%0A%09%5E%20true.%20'),
messageSends: ["ifFalse:", "log:", "ajax:options:", "apiUrlFor:", unescape("-%3E"), "meta", "data", "value:"],
referencedClasses: []
}),
smalltalk.Issues);

View File

@ -0,0 +1,61 @@
smalltalk.addPackage('Hubboard-Tests', {});
smalltalk.addClass('HubboardAppTests', smalltalk.TestCase, [], 'Hubboard-Tests');
smalltalk.addMethod(
unescape('_testIsInProgressWithNoLabels'),
smalltalk.method({
selector: unescape('testIsInProgressWithNoLabels'),
fn: function (){
var self=this;
var result=nil;
(result=smalltalk.send(smalltalk.send((smalltalk.HubboardApp || HubboardApp), "_current", []), "_inProgress_", [smalltalk.send((smalltalk.Array || Array), "_new", [])]));
smalltalk.send(self, "_assert_equals_", [false, result]);
return self;}
}),
smalltalk.HubboardAppTests);
smalltalk.addMethod(
unescape('_testIsInProgressWithLabels'),
smalltalk.method({
selector: unescape('testIsInProgressWithLabels'),
fn: function (){
var self=this;
var result=nil;
var mocklabel=nil;
(mocklabel=(function($rec){smalltalk.send($rec, "_at_put_", ["name", unescape("in-progress")]);return smalltalk.send($rec, "_yourself", []);})(smalltalk.send((smalltalk.Dictionary || Dictionary), "_new", [])));
(result=smalltalk.send(smalltalk.send((smalltalk.HubboardApp || HubboardApp), "_current", []), "_inProgress_", [(function($rec){smalltalk.send($rec, "_add_", [mocklabel]);return smalltalk.send($rec, "_yourself", []);})(smalltalk.send((smalltalk.Array || Array), "_new", []))]));
smalltalk.send(self, "_assert_equals_", [true, result]);
return self;}
}),
smalltalk.HubboardAppTests);
smalltalk.addClass('IssueTileTests', smalltalk.TestCase, ['object'], 'Hubboard-Tests');
smalltalk.addMethod(
unescape('_testParsingProjectFromUrl'),
smalltalk.method({
selector: unescape('testParsingProjectFromUrl'),
fn: function (){
var self=this;
var url=nil;
(url=unescape("https%3A//github.com/rtyler/resin/issues/1"));
(result=smalltalk.send(self['@object'], "_parseUrl_", [url]));
smalltalk.send(self, "_assert_equals_", ["rtyler", smalltalk.send((typeof result == 'undefined' ? nil : result), "_at_", ["owner"])]);
smalltalk.send(self, "_assert_equals_", ["resin", smalltalk.send((typeof result == 'undefined' ? nil : result), "_at_", ["project"])]);
return self;}
}),
smalltalk.IssueTileTests);
smalltalk.addMethod(
unescape('_setUp'),
smalltalk.method({
selector: unescape('setUp'),
fn: function (){
var self=this;
(self['@object']=smalltalk.send((smalltalk.IssueTile || IssueTile), "_new", []));
return self;}
}),
smalltalk.IssueTileTests);

81
js/Hubboard-Tests.js Normal file
View File

@ -0,0 +1,81 @@
smalltalk.addPackage('Hubboard-Tests', {});
smalltalk.addClass('HubboardAppTests', smalltalk.TestCase, [], 'Hubboard-Tests');
smalltalk.addMethod(
unescape('_testIsInProgressWithNoLabels'),
smalltalk.method({
selector: unescape('testIsInProgressWithNoLabels'),
category: 'not yet classified',
fn: function (){
var self=this;
var result=nil;
(result=smalltalk.send(smalltalk.send((smalltalk.HubboardApp || HubboardApp), "_current", []), "_inProgress_", [smalltalk.send((smalltalk.Array || Array), "_new", [])]));
smalltalk.send(self, "_assert_equals_", [false, result]);
return self;},
args: [],
source: unescape('testIsInProgressWithNoLabels%0A%09%22Verify%20we%20say%20that%20this%20thing%20is%20not%20in%20progress%20if%20there%20are%20no%20labels%22%0A%09%7C%20result%20%7C%0A%09result%20%3A%3D%20HubboardApp%20current%20inProgress%3A%20%28Array%20new%29.%0A%09self%20assert%3A%20false%20equals%3A%20result.'),
messageSends: ["inProgress:", "current", "new", "assert:equals:"],
referencedClasses: ["HubboardApp", "Array"]
}),
smalltalk.HubboardAppTests);
smalltalk.addMethod(
unescape('_testIsInProgressWithLabels'),
smalltalk.method({
selector: unescape('testIsInProgressWithLabels'),
category: 'not yet classified',
fn: function (){
var self=this;
var result=nil;
var mocklabel=nil;
(mocklabel=(function($rec){smalltalk.send($rec, "_at_put_", ["name", unescape("in-progress")]);return smalltalk.send($rec, "_yourself", []);})(smalltalk.send((smalltalk.Dictionary || Dictionary), "_new", [])));
(result=smalltalk.send(smalltalk.send((smalltalk.HubboardApp || HubboardApp), "_current", []), "_inProgress_", [(function($rec){smalltalk.send($rec, "_add_", [mocklabel]);return smalltalk.send($rec, "_yourself", []);})(smalltalk.send((smalltalk.Array || Array), "_new", []))]));
smalltalk.send(self, "_assert_equals_", [true, result]);
return self;},
args: [],
source: unescape('testIsInProgressWithLabels%0A%09%22Verify%20we%20say%20that%20this%20thing%20is%20not%20in%20progress%20if%20there%20are%20no%20labels%22%0A%09%7C%20result%20mocklabel%20%7C%0A%09mocklabel%20%3A%3D%20Dictionary%20new%20at%3A%20%27name%27%20put%3A%20%27in-progress%27%3B%20yourself.%0A%09result%20%3A%3D%20HubboardApp%20current%20inProgress%3A%20%28Array%20new%20add%3A%20mocklabel%3B%20yourself%29.%0A%09self%20assert%3A%20true%20equals%3A%20result.'),
messageSends: ["at:put:", "yourself", "new", "inProgress:", "current", "add:", "assert:equals:"],
referencedClasses: ["Dictionary", "HubboardApp", "Array"]
}),
smalltalk.HubboardAppTests);
smalltalk.addClass('IssueTileTests', smalltalk.TestCase, ['object'], 'Hubboard-Tests');
smalltalk.addMethod(
unescape('_testParsingProjectFromUrl'),
smalltalk.method({
selector: unescape('testParsingProjectFromUrl'),
category: 'not yet classified',
fn: function (){
var self=this;
var url=nil;
(url=unescape("https%3A//github.com/rtyler/resin/issues/1"));
(result=smalltalk.send(self['@object'], "_parseUrl_", [url]));
smalltalk.send(self, "_assert_equals_", ["rtyler", smalltalk.send((typeof result == 'undefined' ? nil : result), "_at_", ["owner"])]);
smalltalk.send(self, "_assert_equals_", ["resin", smalltalk.send((typeof result == 'undefined' ? nil : result), "_at_", ["project"])]);
return self;},
args: [],
source: unescape('testParsingProjectFromUrl%0A%09%22Verify%20we%20can%20parse%20the%20html_url%20for%20an%20issue%20into%20the%20owner/project%20pairs%22%0A%09%7C%20url%20%7C%0A%09url%20%3A%3D%20%27https%3A//github.com/rtyler/resin/issues/1%27.%0A%09result%20%3A%3D%20object%20parseUrl%3A%20url.%0A%09self%20assert%3A%20%27rtyler%27%20equals%3A%20%28result%20at%3A%20%27owner%27%29.%0A%09self%20assert%3A%20%27resin%27%20equals%3A%20%28result%20at%3A%20%27project%27%29.%0A%09'),
messageSends: ["parseUrl:", "assert:equals:", "at:"],
referencedClasses: []
}),
smalltalk.IssueTileTests);
smalltalk.addMethod(
unescape('_setUp'),
smalltalk.method({
selector: unescape('setUp'),
category: 'not yet classified',
fn: function (){
var self=this;
(self['@object']=smalltalk.send((smalltalk.IssueTile || IssueTile), "_new", []));
return self;},
args: [],
source: unescape('setUp%0A%09object%20%3A%3D%20IssueTile%20new.'),
messageSends: ["new"],
referencedClasses: ["IssueTile"]
}),
smalltalk.IssueTileTests);

127
js/Hubboard.deploy.js Normal file
View File

@ -0,0 +1,127 @@
smalltalk.addPackage('Hubboard', {});
smalltalk.addClass('IssueTile', smalltalk.Widget, ['raw', 'title', 'body', 'issueId', 'number', 'project', 'projectOwner', 'issueStatus'], 'Hubboard');
smalltalk.addMethod(
unescape('_renderOn_'),
smalltalk.method({
selector: unescape('renderOn%3A'),
fn: function (html){
var self=this;
var numberClass=nil;
(numberClass="number");
((($receiver = smalltalk.send(self['@issueStatus'], "__eq", [smalltalk.symbolFor("open")])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (numberClass=smalltalk.send(numberClass, "__comma", [" open"]));})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return (numberClass=smalltalk.send(numberClass, "__comma", [" open"]));})]));
((($receiver = smalltalk.send(self['@issueStatus'], "__eq", [smalltalk.symbolFor("inprogress")])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (numberClass=smalltalk.send(numberClass, "__comma", [" inprogress"]));})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return (numberClass=smalltalk.send(numberClass, "__comma", [" inprogress"]));})]));
(function($rec){smalltalk.send($rec, "_class_", [smalltalk.send("issuetile issuetile_", "__comma", [smalltalk.send(self['@issueId'], "_asString", [])])]);return smalltalk.send($rec, "_with_", [(function(){(function($rec){smalltalk.send($rec, "_class_", [numberClass]);return smalltalk.send($rec, "_with_", [(function(){(function($rec){smalltalk.send($rec, "_href_", [smalltalk.send(self['@raw'], "_at_", ["html_url"])]);smalltalk.send($rec, "_target_", ["_blank"]);return smalltalk.send($rec, "_with_", [smalltalk.send(unescape("%23"), "__comma", [smalltalk.send(self['@number'], "_asString", [])])]);})(smalltalk.send(html, "_a", []));smalltalk.send(html, "_with_", [" in "]);(function($rec){smalltalk.send($rec, "_href_", [smalltalk.send(unescape("https%3A//github.com/"), "__comma", [self['@projectOwner']])]);return smalltalk.send($rec, "_with_", [self['@projectOwner']]);})(smalltalk.send(html, "_a", []));smalltalk.send(html, "_with_", [unescape("/")]);return (function($rec){smalltalk.send($rec, "_href_", [smalltalk.send(smalltalk.send(smalltalk.send(unescape("https%3A//github.com/"), "__comma", [self['@projectOwner']]), "__comma", [unescape("/")]), "__comma", [self['@project']])]);return smalltalk.send($rec, "_with_", [self['@project']]);})(smalltalk.send(html, "_a", []));})]);})(smalltalk.send(html, "_div", []));(function($rec){smalltalk.send($rec, "_class_", ["title"]);return smalltalk.send($rec, "_with_", [self['@title']]);})(smalltalk.send(html, "_span", []));return smalltalk.send(html, "_br", []);})]);})(smalltalk.send(html, "_div", []));
return self;}
}),
smalltalk.IssueTile);
smalltalk.addMethod(
unescape('_withData_'),
smalltalk.method({
selector: unescape('withData%3A'),
fn: function (dataHash){
var self=this;
var issueInfo=nil;
(self['@raw']=dataHash);
(self['@issueId']=smalltalk.send(dataHash, "_at_", ["id"]));
(self['@number']=smalltalk.send(dataHash, "_at_", ["number"]));
(self['@title']=smalltalk.send(dataHash, "_at_", ["title"]));
(self['@body']=smalltalk.send(dataHash, "_at_", ["body"]));
(issueInfo=smalltalk.send(self, "_parseUrl_", [smalltalk.send(dataHash, "_at_", ["html_url"])]));
(self['@project']=smalltalk.send(issueInfo, "_at_", ["project"]));
(self['@projectOwner']=smalltalk.send(issueInfo, "_at_", ["owner"]));
return self;}
}),
smalltalk.IssueTile);
smalltalk.addMethod(
unescape('_parseUrl_'),
smalltalk.method({
selector: unescape('parseUrl%3A'),
fn: function (aUrl){
var self=this;
var parts=nil;
(parts=aUrl.split('/'));
return smalltalk.HashedCollection._fromPairs_([smalltalk.send("owner", "__minus_gt", [smalltalk.send(parts, "_at_", [(4)])]),smalltalk.send("project", "__minus_gt", [smalltalk.send(parts, "_at_", [(5)])])]);
return self;}
}),
smalltalk.IssueTile);
smalltalk.addMethod(
unescape('_setOpen'),
smalltalk.method({
selector: unescape('setOpen'),
fn: function (){
var self=this;
(self['@issueStatus']=smalltalk.symbolFor("open"));
return self;}
}),
smalltalk.IssueTile);
smalltalk.addMethod(
unescape('_setInProgress'),
smalltalk.method({
selector: unescape('setInProgress'),
fn: function (){
var self=this;
(self['@issueStatus']=smalltalk.symbolFor("inprogress"));
return self;}
}),
smalltalk.IssueTile);
smalltalk.addClass('HubboardApp', smalltalk.Object, ['token'], 'Hubboard');
smalltalk.addMethod(
unescape('_initialize'),
smalltalk.method({
selector: unescape('initialize'),
fn: function (){
var self=this;
(self['@token']=smalltalk.send((typeof window == 'undefined' ? nil : window), "_at_", ["github_access_token"]));
return self;}
}),
smalltalk.HubboardApp);
smalltalk.addMethod(
unescape('_bootstrap'),
smalltalk.method({
selector: unescape('bootstrap'),
fn: function (){
var self=this;
var api=nil;
(api=smalltalk.send(smalltalk.send((smalltalk.Issues || Issues), "_new", []), "_setToken_", [self['@token']]));
smalltalk.send(api, "_issues_loadAll_", [(function(issues){return smalltalk.send(issues, "_do_", [(function(issue){var tile=nil;
(tile=smalltalk.send(smalltalk.send((smalltalk.IssueTile || IssueTile), "_new", []), "_withData_", [issue]));return ((($receiver = smalltalk.send(self, "_inProgress_", [smalltalk.send(issue, "_at_", ["labels"])])).klass === smalltalk.Boolean) ? (! $receiver ? (function(){smalltalk.send(tile, "_setOpen", []);return smalltalk.send(tile, "_appendToJQuery_", [smalltalk.send(unescape("%23openissues"), "_asJQuery", [])]);})() : (function(){smalltalk.send(tile, "_setInProgress", []);return smalltalk.send(tile, "_appendToJQuery_", [smalltalk.send(unescape("%23inprogressissues"), "_asJQuery", [])]);})()) : smalltalk.send($receiver, "_ifFalse_ifTrue_", [(function(){smalltalk.send(tile, "_setOpen", []);return smalltalk.send(tile, "_appendToJQuery_", [smalltalk.send(unescape("%23openissues"), "_asJQuery", [])]);}), (function(){smalltalk.send(tile, "_setInProgress", []);return smalltalk.send(tile, "_appendToJQuery_", [smalltalk.send(unescape("%23inprogressissues"), "_asJQuery", [])]);})]));})]);}), false]);
return self;}
}),
smalltalk.HubboardApp);
smalltalk.addMethod(
unescape('_inProgress_'),
smalltalk.method({
selector: unescape('inProgress%3A'),
fn: function (arrayOfLabels){
var self=this;
try{(($receiver = arrayOfLabels) == nil || $receiver == undefined) ? (function(){return (function(){throw({name: 'stReturn', selector: '_inProgress_', fn: function(){return false}})})();})() : $receiver;
smalltalk.send(arrayOfLabels, "_do_", [(function(label){return ((($receiver = smalltalk.send(smalltalk.send(label, "_at_", ["name"]), "__eq", [unescape("in-progress")])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (function(){throw({name: 'stReturn', selector: '_inProgress_', fn: function(){return true}})})();})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return (function(){throw({name: 'stReturn', selector: '_inProgress_', fn: function(){return true}})})();})]));})]);
(function(){throw({name: 'stReturn', selector: '_inProgress_', fn: function(){return false}})})();
return self;
} catch(e) {if(e.name === 'stReturn' && e.selector === '_inProgress_'){return e.fn()} throw(e)}}
}),
smalltalk.HubboardApp);
smalltalk.HubboardApp.klass.iVarNames = ['current'];
smalltalk.addMethod(
unescape('_current'),
smalltalk.method({
selector: unescape('current'),
fn: function (){
var self=this;
return (($receiver = self['@current']) == nil || $receiver == undefined) ? (function(){return (self['@current']=smalltalk.send(self, "_new", [], smalltalk.Object.klass));})() : $receiver;
return self;}
}),
smalltalk.HubboardApp.klass);

172
js/Hubboard.js Normal file
View File

@ -0,0 +1,172 @@
smalltalk.addPackage('Hubboard', {});
smalltalk.addClass('IssueTile', smalltalk.Widget, ['raw', 'title', 'body', 'issueId', 'number', 'project', 'projectOwner', 'issueStatus'], 'Hubboard');
smalltalk.addMethod(
unescape('_renderOn_'),
smalltalk.method({
selector: unescape('renderOn%3A'),
category: 'not yet classified',
fn: function (html){
var self=this;
var numberClass=nil;
(numberClass="number");
((($receiver = smalltalk.send(self['@issueStatus'], "__eq", [smalltalk.symbolFor("open")])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (numberClass=smalltalk.send(numberClass, "__comma", [" open"]));})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return (numberClass=smalltalk.send(numberClass, "__comma", [" open"]));})]));
((($receiver = smalltalk.send(self['@issueStatus'], "__eq", [smalltalk.symbolFor("inprogress")])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (numberClass=smalltalk.send(numberClass, "__comma", [" inprogress"]));})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return (numberClass=smalltalk.send(numberClass, "__comma", [" inprogress"]));})]));
(function($rec){smalltalk.send($rec, "_class_", [smalltalk.send("issuetile issuetile_", "__comma", [smalltalk.send(self['@issueId'], "_asString", [])])]);return smalltalk.send($rec, "_with_", [(function(){(function($rec){smalltalk.send($rec, "_class_", [numberClass]);return smalltalk.send($rec, "_with_", [(function(){(function($rec){smalltalk.send($rec, "_href_", [smalltalk.send(self['@raw'], "_at_", ["html_url"])]);smalltalk.send($rec, "_target_", ["_blank"]);return smalltalk.send($rec, "_with_", [smalltalk.send(unescape("%23"), "__comma", [smalltalk.send(self['@number'], "_asString", [])])]);})(smalltalk.send(html, "_a", []));smalltalk.send(html, "_with_", [" in "]);(function($rec){smalltalk.send($rec, "_href_", [smalltalk.send(unescape("https%3A//github.com/"), "__comma", [self['@projectOwner']])]);return smalltalk.send($rec, "_with_", [self['@projectOwner']]);})(smalltalk.send(html, "_a", []));smalltalk.send(html, "_with_", [unescape("/")]);return (function($rec){smalltalk.send($rec, "_href_", [smalltalk.send(smalltalk.send(smalltalk.send(unescape("https%3A//github.com/"), "__comma", [self['@projectOwner']]), "__comma", [unescape("/")]), "__comma", [self['@project']])]);return smalltalk.send($rec, "_with_", [self['@project']]);})(smalltalk.send(html, "_a", []));})]);})(smalltalk.send(html, "_div", []));(function($rec){smalltalk.send($rec, "_class_", ["title"]);return smalltalk.send($rec, "_with_", [self['@title']]);})(smalltalk.send(html, "_span", []));return smalltalk.send(html, "_br", []);})]);})(smalltalk.send(html, "_div", []));
return self;},
args: ["html"],
source: unescape('renderOn%3A%20html%0A%09%7C%20numberClass%20%7C%0A%09numberClass%20%3A%3D%20%27number%27.%0A%0A%09issueStatus%20%3D%20%23open%20ifTrue%3A%20%5B%20numberClass%20%3A%3D%20numberClass%2C%20%27%20open%27%20%5D.%20%0A%09issueStatus%20%3D%20%23inprogress%20ifTrue%3A%20%5B%20numberClass%20%3A%3D%20numberClass%2C%20%27%20inprogress%27%20%5D.%20%0A%0A%09html%20div%0A%09%09class%3A%20%28%27issuetile%20issuetile_%27%2C%20issueId%20asString%29%3B%0A%09%09with%3A%20%5B%0A%09%09%09html%20div%0A%09%09%09%09class%3A%20numberClass%3B%0A%09%09%09%09with%3A%20%5B%20%0A%09%09%09%09%09html%20a%20href%3A%20%28raw%20at%3A%20%27html_url%27%29%3B%20target%3A%20%27_blank%27%3B%20with%3A%20%28%27%23%27%2C%20number%20asString%29.%0A%09%09%09%09%09html%20with%3A%20%27%20in%20%27.%0A%09%09%09%09%09html%20a%20href%3A%20%28%27https%3A//github.com/%27%2C%20projectOwner%29%3B%20with%3A%20projectOwner.%0A%09%09%09%09%09html%20with%3A%20%27/%27.%0A%09%09%09%09%09html%20%20a%20href%3A%20%28%27https%3A//github.com/%27%2C%20projectOwner%2C%20%27/%27%2C%20project%29%3B%20with%3A%20project.%0A%09%09%09%09%5D.%0A%09%09%09html%20span%20class%3A%20%27title%27%3B%20with%3A%20title.%0A%09%09%09html%20br.%0A%09%09%5D.'),
messageSends: ["ifTrue:", unescape("%3D"), unescape("%2C"), "class:", "asString", "with:", "href:", "at:", "target:", "a", "div", "span", "br"],
referencedClasses: []
}),
smalltalk.IssueTile);
smalltalk.addMethod(
unescape('_withData_'),
smalltalk.method({
selector: unescape('withData%3A'),
category: 'not yet classified',
fn: function (dataHash){
var self=this;
var issueInfo=nil;
(self['@raw']=dataHash);
(self['@issueId']=smalltalk.send(dataHash, "_at_", ["id"]));
(self['@number']=smalltalk.send(dataHash, "_at_", ["number"]));
(self['@title']=smalltalk.send(dataHash, "_at_", ["title"]));
(self['@body']=smalltalk.send(dataHash, "_at_", ["body"]));
(issueInfo=smalltalk.send(self, "_parseUrl_", [smalltalk.send(dataHash, "_at_", ["html_url"])]));
(self['@project']=smalltalk.send(issueInfo, "_at_", ["project"]));
(self['@projectOwner']=smalltalk.send(issueInfo, "_at_", ["owner"]));
return self;},
args: ["dataHash"],
source: unescape('withData%3A%20dataHash%0A%09%22Set%20up%20a%20the%20tile%20with%20data%20from%20the%20GitHub%20API%22%0A%09%7C%20issueInfo%20%7C%0A%09raw%20%3A%3D%20dataHash.%0A%09issueId%20%3A%3D%20dataHash%20at%3A%20%27id%27.%0A%09number%20%3A%3D%20dataHash%20at%3A%20%27number%27.%0A%09title%20%3A%3D%20dataHash%20at%3A%20%27title%27.%0A%09body%20%3A%3D%20dataHash%20at%3A%20%27body%27.%0A%0A%09issueInfo%20%3A%3D%20self%20parseUrl%3A%20%28dataHash%20at%3A%20%27html_url%27%29.%0A%09project%20%3A%3D%20issueInfo%20at%3A%20%27project%27.%0A%09projectOwner%20%3A%3D%20issueInfo%20at%3A%20%27owner%27.'),
messageSends: ["at:", "parseUrl:"],
referencedClasses: []
}),
smalltalk.IssueTile);
smalltalk.addMethod(
unescape('_parseUrl_'),
smalltalk.method({
selector: unescape('parseUrl%3A'),
category: 'not yet classified',
fn: function (aUrl){
var self=this;
var parts=nil;
(parts=aUrl.split('/'));
return smalltalk.HashedCollection._fromPairs_([smalltalk.send("owner", "__minus_gt", [smalltalk.send(parts, "_at_", [(4)])]),smalltalk.send("project", "__minus_gt", [smalltalk.send(parts, "_at_", [(5)])])]);
return self;},
args: ["aUrl"],
source: unescape('parseUrl%3A%20aUrl%0A%09%22Return%20a%20Hash%20with%20the%20%27owner%27%20and%20%27project%27%20based%20on%20the%20given%20Issue%20URL%22%0A%09%7C%20parts%20%7C%0A%09parts%20%3A%3D%20%3CaUrl.split%28%27/%27%29%3E.%0A%09%0A%09%5E%20%23%7B%27owner%27%20-%3E%20%28parts%20at%3A%204%29.%0A%09%09%27project%27%20-%3E%20%28parts%20at%3A%205%29%7D.'),
messageSends: [unescape("-%3E"), "at:"],
referencedClasses: []
}),
smalltalk.IssueTile);
smalltalk.addMethod(
unescape('_setOpen'),
smalltalk.method({
selector: unescape('setOpen'),
category: 'not yet classified',
fn: function (){
var self=this;
(self['@issueStatus']=smalltalk.symbolFor("open"));
return self;},
args: [],
source: unescape('setOpen%0A%09%22Set%20this%20issue%20as%20an%20open%20issue%22%0A%09issueStatus%20%3A%3D%20%23open.'),
messageSends: [],
referencedClasses: []
}),
smalltalk.IssueTile);
smalltalk.addMethod(
unescape('_setInProgress'),
smalltalk.method({
selector: unescape('setInProgress'),
category: 'not yet classified',
fn: function (){
var self=this;
(self['@issueStatus']=smalltalk.symbolFor("inprogress"));
return self;},
args: [],
source: unescape('setInProgress%0A%09%22Set%20this%20issue%20as%20an%20inprogress%20issue%22%0A%09issueStatus%20%3A%3D%20%23inprogress.'),
messageSends: [],
referencedClasses: []
}),
smalltalk.IssueTile);
smalltalk.addClass('HubboardApp', smalltalk.Object, ['token'], 'Hubboard');
smalltalk.addMethod(
unescape('_initialize'),
smalltalk.method({
selector: unescape('initialize'),
category: 'not yet classified',
fn: function (){
var self=this;
(self['@token']=smalltalk.send((typeof window == 'undefined' ? nil : window), "_at_", ["github_access_token"]));
return self;},
args: [],
source: unescape('initialize%0A%09token%20%3A%3D%20window%20at%3A%20%27github_access_token%27.'),
messageSends: ["at:"],
referencedClasses: []
}),
smalltalk.HubboardApp);
smalltalk.addMethod(
unescape('_bootstrap'),
smalltalk.method({
selector: unescape('bootstrap'),
category: 'not yet classified',
fn: function (){
var self=this;
var api=nil;
(api=smalltalk.send(smalltalk.send((smalltalk.Issues || Issues), "_new", []), "_setToken_", [self['@token']]));
smalltalk.send(api, "_issues_loadAll_", [(function(issues){return smalltalk.send(issues, "_do_", [(function(issue){var tile=nil;
(tile=smalltalk.send(smalltalk.send((smalltalk.IssueTile || IssueTile), "_new", []), "_withData_", [issue]));return ((($receiver = smalltalk.send(self, "_inProgress_", [smalltalk.send(issue, "_at_", ["labels"])])).klass === smalltalk.Boolean) ? (! $receiver ? (function(){smalltalk.send(tile, "_setOpen", []);return smalltalk.send(tile, "_appendToJQuery_", [smalltalk.send(unescape("%23openissues"), "_asJQuery", [])]);})() : (function(){smalltalk.send(tile, "_setInProgress", []);return smalltalk.send(tile, "_appendToJQuery_", [smalltalk.send(unescape("%23inprogressissues"), "_asJQuery", [])]);})()) : smalltalk.send($receiver, "_ifFalse_ifTrue_", [(function(){smalltalk.send(tile, "_setOpen", []);return smalltalk.send(tile, "_appendToJQuery_", [smalltalk.send(unescape("%23openissues"), "_asJQuery", [])]);}), (function(){smalltalk.send(tile, "_setInProgress", []);return smalltalk.send(tile, "_appendToJQuery_", [smalltalk.send(unescape("%23inprogressissues"), "_asJQuery", [])]);})]));})]);}), false]);
return self;},
args: [],
source: unescape('bootstrap%0A%09%7C%20api%20%7C%0A%09api%20%3A%3D%20Issues%20new%20setToken%3A%20token.%0A%09api%20issues%3A%20%5B%20%3Aissues%20%7C%0A%09issues%20do%3A%20%5B%20%3Aissue%20%7C%0A%09%09%09%7C%20tile%20%7C%0A%09%09%09tile%20%3A%3D%20IssueTile%20new%20withData%3A%20issue.%0A%09%09%09%28self%20inProgress%3A%20%28issue%20at%3A%20%27labels%27%29%29%0A%09%09%09%09%09ifFalse%3A%20%5B%20tile%20setOpen.%20tile%20appendToJQuery%3A%20%28%27%23openissues%27%20asJQuery%29%20%5D%0A%09%09%09%09%09ifTrue%3A%20%5B%20tile%20setInProgress.%20tile%20appendToJQuery%3A%20%28%27%23inprogressissues%27%20asJQuery%29%20%5D%0A%09%5D%5D%20loadAll%3A%20false.'),
messageSends: ["setToken:", "new", "issues:loadAll:", "do:", "withData:", "ifFalse:ifTrue:", "inProgress:", "at:", "setOpen", "appendToJQuery:", "asJQuery", "setInProgress"],
referencedClasses: ["Issues", "IssueTile"]
}),
smalltalk.HubboardApp);
smalltalk.addMethod(
unescape('_inProgress_'),
smalltalk.method({
selector: unescape('inProgress%3A'),
category: 'not yet classified',
fn: function (arrayOfLabels){
var self=this;
try{(($receiver = arrayOfLabels) == nil || $receiver == undefined) ? (function(){return (function(){throw({name: 'stReturn', selector: '_inProgress_', fn: function(){return false}})})();})() : $receiver;
smalltalk.send(arrayOfLabels, "_do_", [(function(label){return ((($receiver = smalltalk.send(smalltalk.send(label, "_at_", ["name"]), "__eq", [unescape("in-progress")])).klass === smalltalk.Boolean) ? ($receiver ? (function(){return (function(){throw({name: 'stReturn', selector: '_inProgress_', fn: function(){return true}})})();})() : nil) : smalltalk.send($receiver, "_ifTrue_", [(function(){return (function(){throw({name: 'stReturn', selector: '_inProgress_', fn: function(){return true}})})();})]));})]);
(function(){throw({name: 'stReturn', selector: '_inProgress_', fn: function(){return false}})})();
return self;
} catch(e) {if(e.name === 'stReturn' && e.selector === '_inProgress_'){return e.fn()} throw(e)}},
args: ["arrayOfLabels"],
source: unescape('inProgress%3A%20arrayOfLabels%0A%09%22Return%20true%20if%20we%20find%20the%20%27in-progress%27%20label%22%0A%09arrayOfLabels%20ifNil%3A%20%5B%20%5E%20false%20%5D.%0A%0A%09arrayOfLabels%20do%3A%20%5B%20%3Alabel%20%7C%0A%09%09%28label%20at%3A%20%27name%27%29%20%3D%20%27in-progress%27%20ifTrue%3A%20%5B%20%5E%20true%20%5D.%0A%09%5D.%0A%09%5E%20false.'),
messageSends: ["ifNil:", "do:", "ifTrue:", unescape("%3D"), "at:"],
referencedClasses: []
}),
smalltalk.HubboardApp);
smalltalk.HubboardApp.klass.iVarNames = ['current'];
smalltalk.addMethod(
unescape('_current'),
smalltalk.method({
selector: unescape('current'),
category: 'not yet classified',
fn: function (){
var self=this;
return (($receiver = self['@current']) == nil || $receiver == undefined) ? (function(){return (self['@current']=smalltalk.send(self, "_new", [], smalltalk.Object.klass));})() : $receiver;
return self;},
args: [],
source: unescape('current%0A%09%5E%20current%20ifNil%3A%20%5B%20current%20%3A%3D%20super%20new%20%5D.'),
messageSends: ["ifNil:", "new"],
referencedClasses: []
}),
smalltalk.HubboardApp.klass);

46
st/GitHub.st Normal file
View File

@ -0,0 +1,46 @@
Smalltalk current createPackage: 'GitHub' properties: #{}!
Object subclass: #Issues
instanceVariableNames: 'token authenticated'
category: 'GitHub'!
!Issues methodsFor: 'not yet classified'!
baseUrl
"Return the GitHub API url base"
^ 'https://api.github.com'.
!
setToken: anAccessToken
"Set the OAuth token we can use"
token := anAccessToken.
authenticated := true.
!
apiUrlFor: apiString
"Return the combined URL with an access token if we have one"
| url |
url := '', (self baseUrl), apiString.
authenticated ifTrue: [ url := '', url, '?access_token=', token ].
^ url.
!
initialize
authenticated := false.
token := nil.
!
issues: aPageCallback loadAll: shouldLoadAll
"Call aPageCallback with every page of issues loaded from GitHub"
authenticated ifFalse: [ console log: 'Cannot load issues unless we have an access token'. ^ false ].
jQuery ajax: (self apiUrlFor: '/issues') options: #{
'dataType' -> 'jsonp'.
'success' -> [ :result |
console log: (result meta).
console log: (result data).
aPageCallback value: (result data).
]
}.
^ true.
! !

41
st/Hubboard-Tests.st Normal file
View File

@ -0,0 +1,41 @@
Smalltalk current createPackage: 'Hubboard-Tests' properties: #{}!
TestCase subclass: #HubboardAppTests
instanceVariableNames: ''
category: 'Hubboard-Tests'!
!HubboardAppTests methodsFor: 'not yet classified'!
testIsInProgressWithNoLabels
"Verify we say that this thing is not in progress if there are no labels"
| result |
result := HubboardApp current inProgress: (Array new).
self assert: false equals: result.
!
testIsInProgressWithLabels
"Verify we say that this thing is not in progress if there are no labels"
| result mocklabel |
mocklabel := Dictionary new at: 'name' put: 'in-progress'; yourself.
result := HubboardApp current inProgress: (Array new add: mocklabel; yourself).
self assert: true equals: result.
! !
TestCase subclass: #IssueTileTests
instanceVariableNames: 'object'
category: 'Hubboard-Tests'!
!IssueTileTests methodsFor: 'not yet classified'!
testParsingProjectFromUrl
"Verify we can parse the html_url for an issue into the owner/project pairs"
| url |
url := 'https://github.com/rtyler/resin/issues/1'.
result := object parseUrl: url.
self assert: 'rtyler' equals: (result at: 'owner').
self assert: 'resin' equals: (result at: 'project').
!
setUp
object := IssueTile new.
! !

105
st/Hubboard.st Normal file
View File

@ -0,0 +1,105 @@
Smalltalk current createPackage: 'Hubboard' properties: #{}!
Widget subclass: #IssueTile
instanceVariableNames: 'raw title body issueId number project projectOwner issueStatus'
category: 'Hubboard'!
!IssueTile methodsFor: 'not yet classified'!
renderOn: html
| numberClass |
numberClass := 'number'.
issueStatus = #open ifTrue: [ numberClass := numberClass, ' open' ].
issueStatus = #inprogress ifTrue: [ numberClass := numberClass, ' inprogress' ].
html div
class: ('issuetile issuetile_', issueId asString);
with: [
html div
class: numberClass;
with: [
html a href: (raw at: 'html_url'); target: '_blank'; with: ('#', number asString).
html with: ' in '.
html a href: ('https://github.com/', projectOwner); with: projectOwner.
html with: '/'.
html a href: ('https://github.com/', projectOwner, '/', project); with: project.
].
html span class: 'title'; with: title.
html br.
].
!
withData: dataHash
"Set up a the tile with data from the GitHub API"
| issueInfo |
raw := dataHash.
issueId := dataHash at: 'id'.
number := dataHash at: 'number'.
title := dataHash at: 'title'.
body := dataHash at: 'body'.
issueInfo := self parseUrl: (dataHash at: 'html_url').
project := issueInfo at: 'project'.
projectOwner := issueInfo at: 'owner'.
!
parseUrl: aUrl
"Return a Hash with the 'owner' and 'project' based on the given Issue URL"
| parts |
parts := <aUrl.split('/')>.
^ #{'owner' -> (parts at: 4).
'project' -> (parts at: 5)}.
!
setOpen
"Set this issue as an open issue"
issueStatus := #open.
!
setInProgress
"Set this issue as an inprogress issue"
issueStatus := #inprogress.
! !
Object subclass: #HubboardApp
instanceVariableNames: 'token'
category: 'Hubboard'!
!HubboardApp methodsFor: 'not yet classified'!
initialize
token := window at: 'github_access_token'.
!
bootstrap
| api |
api := Issues new setToken: token.
api issues: [ :issues |
issues do: [ :issue |
| tile |
tile := IssueTile new withData: issue.
(self inProgress: (issue at: 'labels'))
ifFalse: [ tile setOpen. tile appendToJQuery: ('#openissues' asJQuery) ]
ifTrue: [ tile setInProgress. tile appendToJQuery: ('#inprogressissues' asJQuery) ]
]] loadAll: false.
!
inProgress: arrayOfLabels
"Return true if we find the 'in-progress' label"
arrayOfLabels ifNil: [ ^ false ].
arrayOfLabels do: [ :label |
(label at: 'name') = 'in-progress' ifTrue: [ ^ true ].
].
^ false.
! !
HubboardApp class instanceVariableNames: 'current'!
!HubboardApp class methodsFor: 'not yet classified'!
current
^ current ifNil: [ current := super new ].
! !

29
views/index.haml Normal file
View File

@ -0,0 +1,29 @@
%html
%head
%title
Hubboard
%script{:type => 'text/javascript'}
window.github_access_token = "#{access_token}";
%link{:media => 'screen',
:type => 'text/css',
:rel => 'stylesheet',
:href => "/css/hub.css?cachebust=#{Time.now.to_i}"}
%body
- if Resin.development?
%div{:style => 'float: right'}
%button{:onclick => 'smalltalk.Browser._open();'}
Run Amber IDE
%div.issuecolumn
%div.header
Open
%div#openissues
%div.issuecolumn
%div.header
In Progress
%div#inprogressissues
%div.issuecolumn
%div.header
Closed/Resolved
%div#closedissues
= embed_amber(:on_ready => 'smalltalk.HubboardApp._current()._bootstrap();')

12
views/login.haml Normal file
View File

@ -0,0 +1,12 @@
%html
%head
%title
Hubboard
%link{:media => 'screen',
:type => 'text/css',
:rel => 'stylesheet',
:href => '/css/login.css'}
%body
%div.login_box
%a{:href => 'https://github.com/login/oauth/authorize?client_id=4f2316de665c34682d33&scope=repo&redirect_uri=http://localhost:4567/oauth'}
Login with GitHub