diff --git a/app.rb b/app.rb new file mode 100644 index 0000000..9dd9d16 --- /dev/null +++ b/app.rb @@ -0,0 +1,173 @@ +$:.unshift File.join(File.dirname(__FILE__),'lib') + +require 'rubygems' +require 'net/http' +require 'sinatra' +require 'json' +require 'erb' +require 'uri' + +require 'User' +require 'Repo' + +DataMapper::Logger.new(STDOUT, :debug) + +disable :show_exceptions +set :environment, :production + +configure :development do + DataMapper.setup(:default, 'sqlite:////Users/lreilly/Projects/github-scores.com/db/dbdb') +end + +configure :production do + DataMapper.setup(:default, 'sqlite:////Users/lreilly/Projects/github-scores.com/db/db.db') +end + +error do + @title = "404" + @text = "Sorry, but this cat is in another castle!" + @display_small_search = false + erb :not_found +end + +get '/' do + begin + if params[:github_url] + @title = 'High Scores' + @github_url = sanitize_input params[:github_url] + @user = get_user_from_github_url(@github_url) + @repo = get_repo_from_github_url(@github_url) + @high_scores = get_high_scores(@user, @repo) + @display_small_search = true + redirect "/#{@user}/#{@repo}/high_scores/" + else + @title = 'High Scores' + @text = 'Please enter a Github repository URL' + @display_small_search = false + erb :index + end + rescue + @title = "404" + @text = "Sorry, but this cat is in another castle!" + @display_small_search = false + erb :not_found + end +end + +get '/recent/?' do + @repos = Repo.all(:limit => 5, :order => [ :updated_at.desc ]) + @display_small_search = true + erb :recent +end + + +get '/credits/?' do + erb :credits +end + +get '/help/?' do + @display_small_search = true + erb :help +end + +get '/about/?' do + @display_small_search = true + erb :about +end + +get '/:user/:repo/?' do + @user = User::create_from_username(params[:user]) + @repo = Repo::create_from_username_and_repo(params[:user], params[:repo]) + @display_small_search = true + erb :repo +end + +get '/:user/?' do + @user = User::create_from_username(params[:user]) + @display_small_search = true + erb :user +end + +not_found do + @title = "404" + @text = "Sorry, but this cat is in another castle!" + erb :not_found +end + +def sanitize_input(url) + url = url.downcase + + # Special rules for Github URLs starting with 'github.com' + if url[0..9] == 'github.com' + url = 'https://www.github.com' + url[9..url.size] + + # Special rules for Github URLs starting with 'www.github.com' + elsif url[0..13] == 'www.github.com' + url = 'https://www.github.com' + url[13..url.size] + end + + # Special rules for Github URLs ending in 'git' + if url[-4,4] == '.git' + url = url[0..-5] + end + + url = url.gsub("http://", "https://") + url = url.gsub("git@github.com:", "https://www.github.com/") + url = url.gsub("git://", "https://www.") + + # If someone just passes in user/repo e.g. leereilly/leereilly.net + tokens = url.split('/') + if tokens.size == 2 + url = "https://www.github.com/#{tokens[0]}/#{tokens[1]}" + end + + return url +end + +def get_user_from_github_url(sanitized_github_url) + return sanitized_github_url.split('/')[3] +end + +def get_repo_from_github_url(sanitized_github_url) + return sanitized_github_url.split('/')[4] +end + +def get_high_scores(user, repo) + begin + # Kludge - three API calls + stored_user = User::create_from_username(user) + puts "Storing user: #{stored_user}" + stored_repo = Repo::create_from_username_and_repo(user, repo) + puts "Storing repo: #{stored_repo}" + + contributors_url = "http://github.com/api/v2/json/repos/show/#{user}/#{repo}/contributors" + + contributors_feed = Net::HTTP.get_response(URI.parse(contributors_url)) + contributors = contributors_feed.body + contributors_result = JSON.parse(contributors) + repository_contributors = contributors_result['contributors'] + contributors_array = Array.new + repository_contributors.each do |repository_contributor| + user_hash = Hash.new + user_hash[:login] = repository_contributor['login'] + user_hash[:email] = repository_contributor['email'] + user_hash[:gravatar_id] = repository_contributor['gravatar_id'] + user_hash[:location] = repository_contributor['location'] + user_hash[:contributions] = repository_contributor['contributions'].to_i + contributors_array << user_hash + end + return contributors_array + rescue + raise "Sorry, this GitHub repository doesn't seem to exist or is private" + end +end + + +get '/:user/:repo/high_scores/?' do + @title = "New" + @user = params[:user] + @repo = params[:repo] + @high_scores = get_high_scores(@user, @repo) + @display_small_search = true + erb :high_scores +end diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..88fffe3 --- /dev/null +++ b/config.ru @@ -0,0 +1,10 @@ +require 'rubygems' +require 'sinatra.rb' + +# Sinatra defines #set at the top level as a way to set application configuration +set :views, File.join(File.dirname(__FILE__), 'app','views') +set :run, false +set :env, (ENV['RACK_ENV'] ? ENV['RACK_ENV'].to_sym : :development) + +require 'app/main' +run Sinatra.application \ No newline at end of file diff --git a/db/db.db b/db/db.db new file mode 100644 index 0000000..cbe814f Binary files /dev/null and b/db/db.db differ diff --git a/lib/Contributor.rb b/lib/Contributor.rb new file mode 100644 index 0000000..d4c3792 --- /dev/null +++ b/lib/Contributor.rb @@ -0,0 +1,48 @@ +require 'rubygems' +require 'data_mapper' +require 'net/http' +require 'json' +require 'uri' + +DataMapper::Logger.new($stdout, :debug) +DataMapper.setup(:default, 'sqlite:////Users/lreilly/Projects/github-scores.com/db/db.db') + +class Contributor + include DataMapper::Resource + + API_VERSION = 'v2' + BASE_URL = 'http://github.com/api/' + API_VERSION + '/json/user/show/' + + property :id, Serial + property :login, String + property :gravatar_id, String + property :contributions, String + + def self.create_from_user_and_repo(user, repo) + stored_user = User::create_from_username(user) + stored_repo = Repo::create_from_username_and_repo(user, repo) + + contributors_url = "http://github.com/api/v2/json/repos/show/#{user}/#{repo}/contributors" + contributors_feed = Net::HTTP.get_response(URI.parse(contributors_url)) + contributors = contributors_feed.body + contributors_result = JSON.parse(contributors) + repository_contributors = contributors_result['contributors'] + contributors_array = Array.new + + repository_contributors.each do |repository_contributor| + contributor = Contributor.new + contributor.login = repository_contributor['login'] + contributor.gravatar_id = repository_contributor['gravatar_id'] + contributor.contributions = repository_contributor['contributions'] + contributor.save + end + end + + def self.get_json_response(url) + Net::HTTP.get_response(URI.parse(url)) + end +end + +DataMapper::auto_upgrade! +contributors = Contributor::create_from_user_and_repo('leereilly', 'leereilly.net') + diff --git a/lib/Repo.rb b/lib/Repo.rb new file mode 100644 index 0000000..0ca0f35 --- /dev/null +++ b/lib/Repo.rb @@ -0,0 +1,84 @@ +require 'rubygems' +require 'data_mapper' +require 'net/http' +require 'json' +require 'uri' + +DataMapper::Logger.new($stdout, :debug) +DataMapper.setup(:default, 'sqlite:////Users/lreilly/Projects/github-scores.com/db/db.db') + +class Repo + include DataMapper::Resource + + API_VERSION = 'v2' + BASE_URL = 'http://github.com/api/' + API_VERSION + '/json/repos/show/' + + property :id, Serial + property :owner, String + property :url, String + property :homepage, String + property :name, String + property :description, String + property :parent, String + property :has_issues, String + property :source, String + property :watchers, String + property :has_downloads, String + property :fork, String + property :forks, String + property :has_wiki, String + property :pushed_at, String + property :open_issues, String + property :updated_at, DateTime + + def self.create_from_username_and_repo(username, repo) + repo_data_url = Repo.get_repo_data_url(username, repo) + + if found_repo = Repo.first(:owner => username, :name => repo) + if Time.now - Time.parse(found_repo.updated_at.to_s) <= 60*60*24 + puts "Repo created less than 24 hours ago. Returning DB record" + return found_repo + else + puts "Updating current repo" + repo = found_repo + end + else + puts "User not found; using web services" + repo = Repo.new + end + + repo_data_response = get_json_response(repo_data_url) + repo_data = JSON.parse(repo_data_response.body) + repo_data = repo_data['repository'] + + repo.owner = repo_data['owner'] + repo.name = repo_data['name'] + repo.url = repo_data['url'] + repo.homepage = repo_data['homepage'] + repo.description = repo_data['description'] + repo.parent = repo_data['parent'] + repo.has_issues = repo_data['has_issues'] + repo.source = repo_data['source'] + repo.watchers = repo_data['watchers'] + repo.has_downloads = repo_data['has_downloads'] + repo.fork = repo_data['fork'] + repo.forks = repo_data['forks'] + repo.has_wiki = repo_data['has_wiki'] + repo.pushed_at = repo_data['pushed_at'] + repo.open_issues = repo_data['open_issues'] + repo.updated_at = Time.now + repo.save! + return repo + end + + def self.get_json_response(url) + Net::HTTP.get_response(URI.parse(url)) + end + + def self.get_repo_data_url(username, repo) + return BASE_URL + username + '/' + repo + end +end + +DataMapper.auto_upgrade! + diff --git a/lib/User.rb b/lib/User.rb new file mode 100644 index 0000000..ecabbae --- /dev/null +++ b/lib/User.rb @@ -0,0 +1,83 @@ +require 'rubygems' +require 'data_mapper' +require 'net/http' +require 'json' +require 'uri' + +DataMapper::Logger.new($stdout, :debug) +DataMapper.setup(:default, 'sqlite:////Users/lreilly/Projects/github-scores.com/db/db.db') + +class User + include DataMapper::Resource + + API_VERSION = 'v2' + BASE_URL = 'http://github.com/api/' + API_VERSION + '/json/user/show/' + + property :id, Serial + property :github_id, String + property :gravatar_id, String + property :login, String + property :email, String + property :name, String + property :blog, String + property :company, String + property :location, String + property :type, String + property :permission, String + property :created_at, String + property :public_repo_count, String + property :public_gist_count, String + property :following_count, String + property :followers_count, String + property :updated_at, DateTime + + + def self.create_from_username(username) + if found_user = User.first(:login => username) + if Time.now - Time.parse(found_user.updated_at.to_s) <= 60*60*24 + puts "User created less than 24 hours ago. Returning DB record" + return found_user + else + puts "Updating current user" + user = found_user + end + else + puts "User not found; using web services" + user = User.new + end + + user_data_url = User.get_user_data_url(username) + user_data_response = get_json_response(user_data_url) + user_data = JSON.parse(user_data_response.body) + user_data = user_data['user'] + + user.github_id = user_data['id'] + user.gravatar_id = user_data['gravatar_id'] + user.login = user_data['login'] + user.email = user_data['email'] + user.name = user_data['name'] + user.blog = user_data['blog'] + user.company = user_data['company'] + user.location = user_data['location'] + user.type = user_data['type'] + user.permission = user_data['permission'] + user.created_at = user_data['created_at'] + user.public_repo_count = user_data['public_repo_count'] + user.public_gist_count = user_data['public_gist_count'] + user.following_count = user_data['following_count'] + user.followers_count = user_data['followers_count'] + user.updated_at = Time.now + user.save! + return user + end + + def self.get_json_response(url) + Net::HTTP.get_response(URI.parse(url)) + end + + def self.get_user_data_url(username) + return BASE_URL + username + end +end + +DataMapper::auto_upgrade! diff --git a/lib/tasks/migrate.task b/lib/tasks/migrate.task new file mode 100644 index 0000000..0b43835 --- /dev/null +++ b/lib/tasks/migrate.task @@ -0,0 +1,3 @@ +task :migrate do + DataMapper.auto_migrate! +end diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..a03b7d0 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,93 @@ +@font-face { + font-family: "ArcadeClassic"; + src: url("../font/ArcadeClassic.otf") format("opentype"); +} + +body { + font-family: "ArcadeClassic"; + letter-spacing: 2px; + word-spacing: 0.5em; + color: white; + background: #252525 url(/img/bg.png) top center no-repeat; +} + +#wrapper { + margin: 0 auto; + width: 700px; +} + +#content { + width: 100%; +} + +a { + text-decoration: none; + color: white; +} + +td { + font-size: 40px; +} + +th { + font-size: 40px; + font-weight: bold; +} + +td.small { + font-size: 20px; + padding:10px; +} + +th.small { + font-size: 20px; + font-weight: bold; + padding:10px; + color: yellow; +} + +table { + vertical-align: center; + margin-left: auto; + margin-right: auto; +} + +input { + color: #FFFFFF; + font-family: ArcadeClassic; + font-weight: bold; + font-size: 24px; + background-color: #72A4D2; +} + +input.small { + font-size: 12px; +} + +#header_search { + text-align: right; + float:right; +} + +.header_search { + text-align: right; + float: right; +} + +p.center { + text-align: center; +} + +h1 { + text-align: lightblue; +} + +h2 { + text-align: center; + color: lightblue; +} + +h3 { + /**text-align: center;*/ + color: lightblue; +} \ No newline at end of file diff --git a/public/font/ArcadeClassic.otf b/public/font/ArcadeClassic.otf new file mode 100644 index 0000000..e346fd6 Binary files /dev/null and b/public/font/ArcadeClassic.otf differ diff --git a/public/img/bg.png b/public/img/bg.png new file mode 100644 index 0000000..e08e47d Binary files /dev/null and b/public/img/bg.png differ diff --git a/public/img/favicon.png b/public/img/favicon.png new file mode 100644 index 0000000..ba3b495 Binary files /dev/null and b/public/img/favicon.png differ diff --git a/public/img/octocat.png b/public/img/octocat.png new file mode 100644 index 0000000..6c42ecb Binary files /dev/null and b/public/img/octocat.png differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..6a80308 --- /dev/null +++ b/public/index.html @@ -0,0 +1,76 @@ + +
+ + + + + + + ++ | ! |
+ + |
15600 | + +mojombo | +San Francisco | ++ |
5200 | +schacon | + +San Francisco, CA | ++ |
3400 | +defunkt | +San Francisco, CA | ++ |
Git is an extremely fast, efficient, distributed version control system ideal for the collaborative development of software.
+ +git·hub is the best way to collaborate with others. Fork, send pull requests and manage all your public and private git repositories.
+ +git·hub·scores is a fun way to rank software project contributors in a 8-bit, 80's-tastic viewing environment.
+ +<%= erb :footer %> \ No newline at end of file diff --git a/views/credits.erb b/views/credits.erb new file mode 100644 index 0000000..f265f9b --- /dev/null +++ b/views/credits.erb @@ -0,0 +1,7 @@ +<%= erb :header %> +Running with Ruby, Sinatra, Passenger on a shared Dreamhost server
+Powered by the Github API
+Written by Lee Reilly
+Octocat logo used with permission
+<%= erb :footer %> \ No newline at end of file diff --git a/views/footer.erb b/views/footer.erb new file mode 100644 index 0000000..0c9db45 --- /dev/null +++ b/views/footer.erb @@ -0,0 +1,11 @@ + +© Lee Reilly 2011 Fork me on Github! TWEET THIS!