Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Layered GitHub API client
Ruby Shell
Fetching latest commit...
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
lib Use a simple accept by default.
.gitignore ignore Gemfile.lock
gh.gemspec change project homepage to github

GH - Layered GitHub API client

Build Status

This is a highly flexible, layered, low-level GitHub client library, trying to get out of your way and let you get to the GitHub data as simple as possible. Unless you add layers, you will end up with Hashes and Arrays. The approach and API should be familiar from projects like Rack or Faraday.

Simple example:

require 'gh'
puts GH['users/rkh']['name']

This will by default use all the middleware that ships with GH, in the following order:

  • GH::Remote - sends HTTP requests to GitHub and parses the response
  • GH::Normalizer - renames fields consistenly, adds hypermedia links if possible
  • GH::LazyLoader - will load missing fields when accessed (handy for dealing with incomplete data without sending to many requests)
  • GH::MergeCommit - adds infos about merge commits to pull request payloads
  • GH::LinkFollower - will add content of hypermedia links as fields (lazyly), allows you to traverse relations
  • GH::Pagination - adds support for transparent pagination
  • GH::Instrumentation - let's you instrument gh

The following middleware is not included by default:

  • GH::Cache - caches the responses (will use Rails cache if in Rails, in-memory cache otherwise)

Main Entry Points

Every layer has two main entry points:

  • [key] - loads data from GitHub
  • load(data) - takes data and applies modifications (handy for dealing with service hook payloads)

These two methods are exposed by any instance of a layer and the GH constant.

Using a Single Layer

You can initialize and use any layer on its own:

gh =
puts gh['users/rkh']['name']

Layers know which other layer they should usually wrap (Remote wraps no other layer, LazyLoader and LinkFollower wrap Normalizer by default, anything else wraps Remote), so you can initialize them right away:

gh =

You can also pass the layer that should be wrapped as an argument:

gh =

Creating Your Own Stack

For convinience a stack DSL is provided:

# Same as
gh = do
  use GH::Normalizer
  use GH::Cache

puts gh['users/rkh']['name']

You can also create reusable Stack instances:

stack = do
  use GH::Normalizer
  use GH::Cache

gh = username: 'rkh', password: 'abc123'
puts gh['user']['name']

One such instance (with the standard setup) can be accessed as GH::DefaultStack


With the main goal to separate authentication from other logic, the gh library supports scopting:

GH.with do
  puts GH['users/rkh']['name']

That way, you could create a stack with, for instance, an access token:

authenticated = token: 'e72e16c7e42f292c6912e7710c838347ae178b4a'

GH.with(authenticated) do
  # ...

Since this is rather common, you can pass options directly to with:

GH.with(username: 'rkh', password: 'abc123') do
  # ...

Scoping is thread-safe.

Is this production ready?

I hope so, we use it in production for Travis CI. The work on this library has been funded by the Travis Love Campaign.

Something went wrong with that request. Please try again.