In this guide, we're going to use the API to fetch information about repositories that we own, and the programming languages that make them up. Then, we'll visualize that information in a couple of different ways using the D3.js library. To interact with the GitHub Enterprise Server API, we'll be using the excellent Ruby library, Octokit.
If you haven't already, you should read the "Basics of Authentication" guide before starting this example. You can find the complete source code for this project in the platform-samples repository.
Let's jump right in!
Setting up an OAuth application
First, register a new application on GitHub Enterprise Server. Set the main and callback
URLs to http://localhost:4567/. As before, we're going to handle authentication for the API by
implementing a Rack middleware using sinatra-auth-github:
require 'sinatra/auth/github'
module Example
class MyGraphApp < Sinatra::Base
# !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
# Instead, set and test environment variables, like below
# if ENV['GITHUB_CLIENT_ID'] && ENV['GITHUB_CLIENT_SECRET']
# CLIENT_ID = ENV['GITHUB_CLIENT_ID']
# CLIENT_SECRET = ENV['GITHUB_CLIENT_SECRET']
# end
CLIENT_ID = ENV['GH_GRAPH_CLIENT_ID']
CLIENT_SECRET = ENV['GH_GRAPH_SECRET_ID']
enable :sessions
set :github_options, {
:scopes => "repo",
:secret => CLIENT_SECRET,
:client_id => CLIENT_ID,
:callback_url => "/"
}
register Sinatra::Auth::Github
get '/' do
if !authenticated?
authenticate!
else
access_token = github_user["token"]
end
end
end
end
Set up a similar config.ru file as in the previous example:
ENV['RACK_ENV'] ||= 'development'
require "rubygems"
require "bundler/setup"
require File.expand_path(File.join(File.dirname(__FILE__), 'server'))
run Example::MyGraphApp
Fetching repository information
This time, in order to talk to the GitHub Enterprise Server API, we're going to use the Octokit Ruby library. This is much easier than directly making a bunch of REST calls. Plus, Octokit was developed by a GitHubber, and is actively maintained, so you know it'll work.
Authentication with the API via Octokit is easy. Just pass your login
and token to the Octokit::Client constructor:
if !authenticated?
authenticate!
else
octokit_client = Octokit::Client.new(:login => github_user.login, :oauth_token => github_user.token)
end
Let's do something interesting with the data about our repositories. We're going to see the different programming languages they use, and count which ones are used most often. To do that, we'll first need a list of our repositories from the API. With Octokit, that looks like this:
repos = client.repositories
Next, we'll iterate over each repository, and count the language that GitHub Enterprise Server associates with it:
language_obj = {}
repos.each do |repo|
# sometimes language can be nil
if repo.language
if !language_obj[repo.language]
language_obj[repo.language] = 1
else
language_obj[repo.language] += 1
end
end
end
languages.to_s
When you restart your server, your web page should display something that looks like this:
{"JavaScript"=>13, "PHP"=>1, "Perl"=>1, "CoffeeScript"=>2, "Python"=>1, "Java"=>3, "Ruby"=>3, "Go"=>1, "C++"=>1}
So far, so good, but not very human-friendly. A visualization would be great in helping us understand how these language counts are distributed. Let's feed our counts into D3 to get a neat bar graph representing the popularity of the languages we use.
Visualizing language counts
D3.js, or just D3, is a comprehensive library for creating many kinds of charts, graphs, and interactive visualizations. Using D3 in detail is beyond the scope of this guide, but for a good introductory article, check out "D3 for Mortals".
D3 is a JavaScript library, and likes working with data as arrays. So, let's convert our Ruby hash into a JSON array for use by JavaScript in the browser.
languages = []
language_obj.each do |lang, count|
languages.push :language => lang, :count => count
end
erb :lang_freq, :locals => { :languages => languages.to_json}
We're simply iterating over each key-value pair in our object and pushing them into
a new array. The reason we didn't do this earlier is because we didn't want to iterate
over our language_obj object while we were creating it.
Now, lang_freq.erb is going to need some JavaScript to support rendering a bar graph. For now, you can just use the code provided here, and refer to the resources linked above if you want to learn more about how D3 works:
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.0.1/d3.v3.min.js"></script>
<style>
svg {
padding: 20px;
}
rect {
fill: #2d578b
}
text {
fill: white;
}
text.yAxis {
font-size: 12px;
font-family: Helvetica, sans-serif;
fill: black;
}
</style>
</head>
<body>
<p>Check this sweet data out:</p>
<div id="lang_freq"></div>
</body>
<script>
var data = <%= languages %>;
var barWidth = 40;
var width = (barWidth + 10) * data.length;
var height = 300;
var x = d3.scale.linear().domain([0, data.length]).range([0, width]);
var y = d3.scale.linear().domain([0, d3.max(data, function(datum) { return datum.count; })]).
rangeRound([0, height]);
// add the canvas to the DOM
var languageBars = d3.select("#lang_freq").
append("svg:svg").
attr("width", width).
attr("height", height);
languageBars.selectAll("rect").
data(data).
enter().
append("svg:rect").
attr("x", function(datum, index) { return x(index); }).
attr("y", function(datum) { return height - y(datum.count); }).
attr("height", function(