gh 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -17,6 +17,7 @@ This will by default use all the middleware that ships with GH, in the following
17
17
  * `GH::LazyLoader` - will load missing fields when accessed (handy for dealing with incomplete data without sending to many requests)
18
18
  * `GH::MergeCommit` - adds infos about merge commits to pull request payloads
19
19
  * `GH::LinkFollower` - will add content of hypermedia links as fields (lazyly), allows you to traverse relations
20
+ * `GH::Pagination` - adds support for transparent pagination
20
21
  * `GH::Instrumentation` - let's you instrument `gh`
21
22
 
22
23
  ## Main Entry Points
data/gh.gemspec CHANGED
@@ -23,4 +23,6 @@ Gem::Specification.new do |s|
23
23
  s.add_runtime_dependency 'backports', '~> 2.3'
24
24
  s.add_runtime_dependency 'multi_json', '~> 1.0'
25
25
  s.add_runtime_dependency 'addressable'
26
+ s.add_runtime_dependency 'net-http-persistent'
27
+ s.add_runtime_dependency 'net-http-pipeline'
26
28
  end
data/lib/gh.rb CHANGED
@@ -6,12 +6,14 @@ module GH
6
6
  autoload :Cache, 'gh/cache'
7
7
  autoload :Case, 'gh/case'
8
8
  autoload :Error, 'gh/error'
9
+ autoload :FaradayAdapter, 'gh/faraday_adapter'
9
10
  autoload :Instrumentation, 'gh/instrumentation'
10
11
  autoload :LazyLoader, 'gh/lazy_loader'
11
12
  autoload :LinkFollower, 'gh/link_follower'
12
13
  autoload :MergeCommit, 'gh/merge_commit'
13
14
  autoload :Normalizer, 'gh/normalizer'
14
15
  autoload :Pagination, 'gh/pagination'
16
+ autoload :Parallel, 'gh/parallel'
15
17
  autoload :Remote, 'gh/remote'
16
18
  autoload :Response, 'gh/response'
17
19
  autoload :ResponseWrapper, 'gh/response_wrapper'
@@ -35,10 +37,11 @@ module GH
35
37
  end
36
38
 
37
39
  extend SingleForwardable
38
- def_delegators :current, :api_host, :[], :reset, :load, :post, :delete, :patch, :put
40
+ def_delegators :current, :api_host, :[], :reset, :load, :post, :delete, :patch, :put, :in_parallel, :in_parallel?
39
41
 
40
42
  DefaultStack = Stack.new do
41
43
  use Instrumentation
44
+ use Parallel
42
45
  use Pagination
43
46
  use LinkFollower
44
47
  use MergeCommit
@@ -43,19 +43,12 @@ module GH
43
43
  clear_partial or clear_all
44
44
  end
45
45
 
46
- # Public: Retrieves resources from Github and caches response for future access.
47
- #
48
- # Examples
49
- #
50
- # Github::Cache.new['users/rkh'] # => { ... }
51
- #
52
- # Returns the Response.
53
- def [](key)
46
+ private
47
+
48
+ def fetch_resource(key)
54
49
  cache.fetch(prefixed(key)) { super }
55
50
  end
56
51
 
57
- private
58
-
59
52
  def clear_partial
60
53
  return false unless cache.respond_to? :delete_matched
61
54
  pattern = "^" << Regexp.escape(prefixed(""))
@@ -18,7 +18,7 @@ module GH
18
18
  private
19
19
 
20
20
  def lazy_load(hash, key, link)
21
- result = modify_hash(backend[link].data, true)
21
+ modify_hash(backend[link].data, true)
22
22
  rescue Exception => error
23
23
  raise Error.new(error, hash)
24
24
  end
@@ -0,0 +1,49 @@
1
+ require 'gh'
2
+
3
+ module GH
4
+ # Public: ...
5
+ class NestedResources < Wrapper
6
+ wraps GH::Normalizer
7
+ double_dispatch
8
+
9
+ def modify_hash(hash, loaded = false)
10
+ hash = super(hash)
11
+ link = hash['_links'].try(:[], 'self') unless loaded
12
+ set_links hash, Addressable::URI.parse(link['href']) if link
13
+ hash
14
+ end
15
+
16
+ def add(hash, link, name, path = name)
17
+ hash["_links"][name] ||= { "href" => nested(link, path) }
18
+ end
19
+
20
+ def nested(link, path)
21
+ new_link = link.dup
22
+ if path.start_with? '/'
23
+ new_link.path = path
24
+ else
25
+ new_link.path += path
26
+ end
27
+ new_link
28
+ end
29
+
30
+ def set_links(hash, link)
31
+ case link.path
32
+ when '/gists'
33
+ add hash, link, 'public'
34
+ add hash, link, 'starred'
35
+ when %r{^/repos/[^/]+/[^/]+$}
36
+ add hash, link, 'commits', 'git/commits'
37
+ add hash, link, 'refs', 'git/refs'
38
+ add hash, link, 'tags', 'git/tags'
39
+ add hash, link, 'issues'
40
+ when %r{^/repos/[^/]+/[^/]+/issues/\d+$}
41
+ add hash, link, 'comments'
42
+ add hash, link, 'events'
43
+ when '/user'
44
+ add hash, link, 'gists', '/gists'
45
+ add hash, link, 'issues', '/issues'
46
+ end
47
+ end
48
+ end
49
+ end
@@ -4,10 +4,7 @@ require 'time'
4
4
  module GH
5
5
  # Public: A Wrapper class that deals with normalizing Github responses.
6
6
  class Normalizer < Wrapper
7
- # Public: Fetches and normalizes a github entity.
8
- #
9
- # Returns normalized Response.
10
- def [](key)
7
+ def generate_response(key, response)
11
8
  result = super
12
9
  links(result)['self'] ||= { 'href' => full_url(key).to_s } if result.respond_to? :to_hash
13
10
  result
@@ -18,6 +18,7 @@ module GH
18
18
  end
19
19
 
20
20
  def [](value)
21
+ raise TypeError, "index has to be an Integer, got #{value.class}" unless value.is_a? Integer
21
22
  return @page[value] if value < @page.size
22
23
  next_page[value - @page.size]
23
24
  end
@@ -32,8 +33,16 @@ module GH
32
33
  wraps GH::Normalizer
33
34
  double_dispatch
34
35
 
36
+ def fetch_resource(key)
37
+ url = full_url(key)
38
+ params = url.query_values || {}
39
+ params['per_page'] ||= 100
40
+ url.query_values = params
41
+ super url.request_uri
42
+ end
43
+
35
44
  def modify_response(response)
36
- return response unless response.headers['link'] =~ /<([^>]+)>;\s*rel=\"next\"/
45
+ return response unless response.respond_to? :to_ary and response.headers['link'] =~ /<([^>]+)>;\s*rel=\"next\"/
37
46
  Paginated.new(response, $1, self)
38
47
  end
39
48
  end
@@ -0,0 +1,55 @@
1
+ require 'gh'
2
+ require 'thread'
3
+ require 'backports/basic_object' unless defined? BasicObject
4
+
5
+ module GH
6
+ # Public: ...
7
+ class Parallel < Wrapper
8
+ class Dummy < BasicObject
9
+ attr_accessor :__delegate__
10
+ def method_missing(*args)
11
+ ::Kernel.raise ::RuntimeError, "response not yet loaded" if __delegate__.nil?
12
+ __delegate__.__send__(*args)
13
+ end
14
+ end
15
+
16
+ def setup(*)
17
+ @in_parallel = false
18
+ @mutex = Mutex.new
19
+ @queue = []
20
+ super
21
+ end
22
+
23
+ def generate_response(key, response)
24
+ return super unless in_parallel?
25
+ dummy = Dummy.new
26
+ @mutex.synchronize { @queue << [dummy, key, response] }
27
+ dummy
28
+ end
29
+
30
+ def in_parallel(&block)
31
+ return yield if in_parallel?
32
+ was, @in_parallel = @in_parallel, true
33
+ result = connection.in_parallel(&block)
34
+ @mutex.synchronize do
35
+ @queue.each { |dummy, key, response| dummy.__delegate__ = backend.generate_response(key, response) }
36
+ @queue.clear
37
+ end
38
+ result
39
+ ensure
40
+ @in_parallel = was unless was.nil?
41
+ end
42
+
43
+ def in_parallel?
44
+ @in_parallel
45
+ end
46
+
47
+ def connection
48
+ @connection ||= begin
49
+ layer = backend
50
+ layer = layer.backend until layer.respond_to? :connection
51
+ layer.connection
52
+ end
53
+ end
54
+ end
55
+ end
@@ -47,7 +47,7 @@ module GH
47
47
  builder.request(:basic_auth, username, password) if username and password
48
48
  builder.request(:retry)
49
49
  builder.response(:raise_error)
50
- builder.adapter(options[:adapter] || :net_http)
50
+ builder.use(options[:adapter] || GH::FaradayAdapter)
51
51
  end
52
52
  end
53
53
 
@@ -56,17 +56,13 @@ module GH
56
56
  "#<#{self.class}: #{api_host}>"
57
57
  end
58
58
 
59
- # Public: Retrieves resources from Github.
60
- #
61
- # Examples
62
- #
63
- # Github::Remote.new['users/rkh'] # => { ... }
64
- #
65
- # Raises Faraday::Error::ResourceNotFound if the resource returns status 404.
66
- # Raises Faraday::Error::ClientError if the resource returns a status between 400 and 599.
67
- # Returns the Response.
68
- def [](key)
69
- response = frontend.http(:get, path_for(key), headers)
59
+ # Internal: ...
60
+ def fetch_resource(key)
61
+ frontend.http(:get, path_for(key), headers)
62
+ end
63
+
64
+ # Internal: ...
65
+ def generate_response(key, response)
70
66
  modify(response.body, response.headers)
71
67
  end
72
68
 
@@ -80,27 +76,27 @@ module GH
80
76
  response = frontend.http(verb, path_for(key), headers) do |req|
81
77
  req.body = Response.new({}, body).to_s if body
82
78
  end
83
- modify(response.body, response.headers)
79
+ frontend.generate_response(key, response)
84
80
  end
85
81
 
86
82
  # Public: ...
87
83
  def post(key, body)
88
- request(:post, key, body)
84
+ frontend.request(:post, key, body)
89
85
  end
90
86
 
91
87
  # Public: ...
92
88
  def delete(key)
93
- request(:delete, key)
89
+ frontend.request(:delete, key)
94
90
  end
95
91
 
96
92
  # Public: ...
97
93
  def patch(key, body)
98
- request(:patch, key, body)
94
+ frontend.request(:patch, key, body)
99
95
  end
100
96
 
101
97
  # Public: ...
102
98
  def put(key, body)
103
- request(:put, key, body)
99
+ frontend.request(:put, key, body)
104
100
  end
105
101
 
106
102
  # Public: ...
@@ -112,6 +108,11 @@ module GH
112
108
  modify(data)
113
109
  end
114
110
 
111
+ # Public: ...
112
+ def in_parallel
113
+ raise RuntimeError, "use GH::Parallel middleware for #in_parallel support"
114
+ end
115
+
115
116
  private
116
117
 
117
118
  def identifier(key)
@@ -1,4 +1,4 @@
1
1
  module GH
2
2
  # Public: Library version.
3
- VERSION = "0.4.2"
3
+ VERSION = "0.5.0"
4
4
  end
@@ -33,6 +33,9 @@ module GH
33
33
  # Internal: ...
34
34
  def_delegator :backend, :http
35
35
 
36
+ # Internal: ...
37
+ def_delegator :backend, :request
38
+
36
39
  # Public: ...
37
40
  def_delegator :backend, :post
38
41
 
@@ -43,7 +46,16 @@ module GH
43
46
  def_delegator :backend, :patch
44
47
 
45
48
  # Public: ...
46
- def_delegator :backendt, :put
49
+ def_delegator :backend, :put
50
+
51
+ # Public: ...
52
+ def_delegator :backend, :fetch_resource
53
+
54
+ # Public: ...
55
+ def_delegator :backend, :in_parallel
56
+
57
+ # Public: ...
58
+ def_delegator :backend, :in_parallel?
47
59
 
48
60
  # Public: Retrieves resources from Github.
49
61
  def self.[](key)
@@ -55,7 +67,12 @@ module GH
55
67
  # By default, this method is delegated to the next layer on the stack
56
68
  # and modify is called.
57
69
  def [](key)
58
- modify backend[key]
70
+ generate_response key, fetch_resource(key)
71
+ end
72
+
73
+ # Internal: ...
74
+ def generate_response(key, resource)
75
+ modify backend.generate_response(key, resource)
59
76
  end
60
77
 
61
78
  # Internal: Get/set default layer to wrap when creating a new instance.
@@ -185,7 +185,6 @@ describe GH::Normalizer do
185
185
  normalized.should include('author')
186
186
  normalized.should include('committer')
187
187
  normalized['author'].should be == 'me'
188
- normalized['author'].should be_equal(normalized['committer'])
189
188
  end
190
189
 
191
190
  it 'copies committer to author' do
@@ -193,7 +192,6 @@ describe GH::Normalizer do
193
192
  normalized.should include('author')
194
193
  normalized.should include('committer')
195
194
  normalized['author'].should be == 'me'
196
- normalized['author'].should be_equal(normalized['committer'])
197
195
  end
198
196
 
199
197
  it 'does not override committer or author if both exist' do
@@ -25,4 +25,8 @@ describe GH::Pagination do
25
25
  data[index].should be == value
26
26
  end
27
27
  end
28
+
29
+ it 'does not wrap hash responses' do
30
+ subject['users/rkh'].should_not be_a(GH::Pagination::Paginated)
31
+ end
28
32
  end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe GH::Parallel do
4
+ before do
5
+ stub_request(:get, "https://api.github.com/users/rkh").to_return(:status => 200, :body => '{"name": "Konstantin Haase"}')
6
+ stub_request(:get, "https://api.github.com/users/svenfuchs").to_return(:status => 200, :body => '{"name": "Sven Fuchs"}')
7
+ stub_request(:get, "https://api.github.com/users/rkh?per_page=100").to_return(:status => 200, :body => '{"name": "Konstantin Haase"}')
8
+ stub_request(:get, "https://api.github.com/users/svenfuchs?per_page=100").to_return(:status => 200, :body => '{"name": "Sven Fuchs"}')
9
+ end
10
+
11
+ it 'allows normal requests' do
12
+ GH['users/rkh']['name'].should be == 'Konstantin Haase'
13
+ end
14
+
15
+ it 'sets in_parallel?' do
16
+ GH.should_not be_in_parallel
17
+ GH.in_parallel { GH.should be_in_parallel }
18
+ GH.should_not be_in_parallel
19
+ end
20
+
21
+ it 'runs requests in parallel' do
22
+ GH::DefaultStack.replace GH::MockBackend, GH::Remote
23
+ GH.current = nil
24
+ GH.should_not be_in_parallel
25
+
26
+ a = b = nil
27
+ GH.in_parallel do
28
+ GH.should be_in_parallel
29
+
30
+ a = GH['users/rkh']
31
+ b = GH['users/svenfuchs']
32
+
33
+ expect { a['name'] }.to raise_error(RuntimeError)
34
+ expect { b['name'] }.to raise_error(RuntimeError)
35
+ end
36
+
37
+ a['name'].should be == "Konstantin Haase"
38
+ b['name'].should be == "Sven Fuchs"
39
+
40
+ a.should respond_to('to_hash')
41
+ b.should respond_to('to_hash')
42
+
43
+ GH.should_not be_in_parallel
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ ---
2
+ - !binary "c2VydmVy": !binary |-
3
+ bmdpbngvMS4wLjEz
4
+ !binary "ZGF0ZQ==": !binary |-
5
+ V2VkLCAyMyBNYXkgMjAxMiAxMzo1OTozNiBHTVQ=
6
+ !binary "Y29udGVudC10eXBl": !binary |-
7
+ YXBwbGljYXRpb24vanNvbjsgY2hhcnNldD11dGYtOA==
8
+ !binary "dHJhbnNmZXItZW5jb2Rpbmc=": !binary |-
9
+ Y2h1bmtlZA==
10
+ !binary "Y29ubmVjdGlvbg==": !binary |-
11
+ a2VlcC1hbGl2ZQ==
12
+ !binary "c3RhdHVz": !binary |-
13
+ MjAwIE9L
14
+ !binary "eC1yYXRlbGltaXQtbGltaXQ=": !binary |-
15
+ NTAwMA==
16
+ !binary "ZXRhZw==": !binary |-
17
+ IjNhMWFkMzc4N2NiZDkwNGQ2NDk4YzllODA5NWZlODY0Ig==
18
+ !binary "bGluaw==": !binary |-
19
+ PGh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvcmtoL3JlcG9zP3BhZ2U9
20
+ MSZwZXJfcGFnZT0xMDA+OyByZWw9ImZpcnN0IiwgPGh0dHBzOi8vYXBpLmdp
21
+ dGh1Yi5jb20vdXNlcnMvcmtoL3JlcG9zP3BhZ2U9MSZwZXJfcGFnZT0xMDA+
22
+ OyByZWw9InByZXYi
23
+ !binary "eC1yYXRlbGltaXQtcmVtYWluaW5n": !binary |-
24
+ NDk5Nw==
25
+ - ! '[{"html_url":"https://github.com/rkh/hpi","pushed_at":"2011-10-28T21:56:35Z","updated_at":"2012-04-10T11:37:21Z","homepage":null,"url":"https://api.github.com/repos/rkh/hpi","has_downloads":true,"watchers":10,"fork":false,"svn_url":"https://github.com/rkh/hpi","has_wiki":true,"has_issues":true,"size":168,"private":false,"mirror_url":null,"forks":1,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"hpi","language":"Ruby","description":null,"clone_url":"https://github.com/rkh/hpi.git","ssh_url":"git@github.com:rkh/hpi.git","git_url":"git://github.com/rkh/hpi.git","created_at":"2011-10-07T23:55:05Z","id":2536005,"open_issues":0},{"html_url":"https://github.com/rkh/tool","pushed_at":"2011-10-27T02:01:35Z","updated_at":"2011-10-27T02:01:35Z","homepage":null,"url":"https://api.github.com/repos/rkh/tool","has_downloads":true,"watchers":1,"fork":false,"svn_url":"https://github.com/rkh/tool","has_wiki":true,"has_issues":true,"size":140,"private":false,"mirror_url":null,"forks":1,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"tool","language":"Ruby","description":null,"clone_url":"https://github.com/rkh/tool.git","ssh_url":"git@github.com:rkh/tool.git","git_url":"git://github.com/rkh/tool.git","created_at":"2011-10-27T01:50:56Z","id":2655239,"open_issues":0},{"html_url":"https://github.com/rkh/twp","pushed_at":"2011-12-15T09:10:41Z","updated_at":"2012-02-11T19:56:48Z","homepage":"http://www.dcl.hpi.uni-potsdam.de/teaching/mds/","url":"https://api.github.com/repos/rkh/twp","has_downloads":true,"watchers":5,"fork":false,"svn_url":"https://github.com/rkh/twp","has_wiki":true,"has_issues":true,"size":256,"private":false,"mirror_url":null,"forks":1,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"twp","language":"Ruby","description":"TWP3
26
+ in Ruby","clone_url":"https://github.com/rkh/twp.git","ssh_url":"git@github.com:rkh/twp.git","git_url":"git://github.com/rkh/twp.git","created_at":"2011-12-11T23:01:21Z","id":2960627,"open_issues":0},{"html_url":"https://github.com/rkh/fibur","pushed_at":"2011-12-19T19:07:32Z","updated_at":"2011-12-19T19:07:33Z","homepage":"","url":"https://api.github.com/repos/rkh/fibur","has_downloads":true,"watchers":1,"fork":true,"svn_url":"https://github.com/rkh/fibur","has_wiki":true,"has_issues":false,"size":112,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"fibur","language":"Ruby","description":"Concurrent
27
+ execution during Ruby I/O","clone_url":"https://github.com/rkh/fibur.git","ssh_url":"git@github.com:rkh/fibur.git","git_url":"git://github.com/rkh/fibur.git","created_at":"2011-12-19T19:06:39Z","id":3014283,"open_issues":0},{"html_url":"https://github.com/rkh/rkelly","pushed_at":"2012-01-09T20:27:16Z","updated_at":"2012-01-09T20:27:16Z","homepage":"","url":"https://api.github.com/repos/rkh/rkelly","has_downloads":true,"watchers":1,"fork":true,"svn_url":"https://github.com/rkh/rkelly","has_wiki":true,"has_issues":false,"size":336,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"rkelly","language":"Ruby","description":"Pure
28
+ ruby javascript parser and interpreter.","clone_url":"https://github.com/rkh/rkelly.git","ssh_url":"git@github.com:rkh/rkelly.git","git_url":"git://github.com/rkh/rkelly.git","created_at":"2012-01-09T20:25:53Z","id":3139761,"open_issues":0},{"html_url":"https://github.com/rkh/curriculum","pushed_at":"2012-01-10T16:30:53Z","updated_at":"2012-01-14T12:17:49Z","homepage":"","url":"https://api.github.com/repos/rkh/curriculum","has_downloads":true,"watchers":1,"fork":true,"svn_url":"https://github.com/rkh/curriculum","has_wiki":true,"has_issues":false,"size":132,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"curriculum","language":"JavaScript","description":"","clone_url":"https://github.com/rkh/curriculum.git","ssh_url":"git@github.com:rkh/curriculum.git","git_url":"git://github.com/rkh/curriculum.git","created_at":"2012-01-10T16:28:11Z","id":3146605,"open_issues":0},{"html_url":"https://github.com/rkh/fog","pushed_at":"2012-01-12T15:40:22Z","updated_at":"2012-02-26T06:24:48Z","homepage":"http://fog.io","url":"https://api.github.com/repos/rkh/fog","has_downloads":true,"watchers":3,"fork":true,"svn_url":"https://github.com/rkh/fog","has_wiki":true,"has_issues":false,"size":180,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"fog","language":"Ruby","description":"The
29
+ Ruby cloud services library.","clone_url":"https://github.com/rkh/fog.git","ssh_url":"git@github.com:rkh/fog.git","git_url":"git://github.com/rkh/fog.git","created_at":"2012-01-12T15:32:01Z","id":3163178,"open_issues":0},{"html_url":"https://github.com/rkh/test-project-1","pushed_at":"2012-05-16T12:08:44Z","updated_at":"2012-05-16T12:08:44Z","homepage":"http://travis-ci.org","url":"https://api.github.com/repos/rkh/test-project-1","has_downloads":true,"watchers":0,"fork":true,"svn_url":"https://github.com/rkh/test-project-1","has_wiki":true,"has_issues":false,"size":152,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"test-project-1","language":"Ruby","description":"Test
30
+ dummy repository for testing Travis CI","clone_url":"https://github.com/rkh/test-project-1.git","ssh_url":"git@github.com:rkh/test-project-1.git","git_url":"git://github.com/rkh/test-project-1.git","created_at":"2012-02-13T15:17:57Z","id":3431064,"open_issues":1},{"html_url":"https://github.com/rkh/oh-my-zsh","pushed_at":"2012-02-22T13:48:52Z","updated_at":"2012-03-07T21:11:13Z","homepage":"http://twitter.com/ohmyzsh","url":"https://api.github.com/repos/rkh/oh-my-zsh","has_downloads":true,"watchers":2,"fork":true,"svn_url":"https://github.com/rkh/oh-my-zsh","has_wiki":true,"has_issues":false,"size":7224,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"oh-my-zsh","language":"Shell","description":"A
31
+ community-driven framework for managing your zsh configuration. Includes 40+ optional
32
+ plugins (rails, git, OSX, hub, capistrano, brew, ant, macports, etc), over 80 terminal
33
+ themes to spice up your morning, and an auto-update tool so that makes it easy
34
+ to keep up with the latest updates from the community.","clone_url":"https://github.com/rkh/oh-my-zsh.git","ssh_url":"git@github.com:rkh/oh-my-zsh.git","git_url":"git://github.com/rkh/oh-my-zsh.git","created_at":"2012-02-22T13:47:54Z","id":3514933,"open_issues":0},{"html_url":"https://github.com/rkh/rbenv-use","pushed_at":"2012-02-22T16:53:26Z","updated_at":"2012-03-27T15:54:20Z","homepage":"","url":"https://api.github.com/repos/rkh/rbenv-use","has_downloads":true,"watchers":10,"fork":false,"svn_url":"https://github.com/rkh/rbenv-use","has_wiki":true,"has_issues":true,"size":108,"private":false,"mirror_url":null,"forks":1,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"rbenv-use","language":"Shell","description":"rbenv
35
+ use rbx","clone_url":"https://github.com/rkh/rbenv-use.git","ssh_url":"git@github.com:rkh/rbenv-use.git","git_url":"git://github.com/rkh/rbenv-use.git","created_at":"2012-02-22T13:51:59Z","id":3514966,"open_issues":0},{"html_url":"https://github.com/rkh/rbenv-whatis","pushed_at":"2012-02-22T13:52:54Z","updated_at":"2012-04-24T20:50:23Z","homepage":null,"url":"https://api.github.com/repos/rkh/rbenv-whatis","has_downloads":true,"watchers":5,"fork":false,"svn_url":"https://github.com/rkh/rbenv-whatis","has_wiki":true,"has_issues":true,"size":84,"private":false,"mirror_url":null,"forks":2,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"rbenv-whatis","language":"Shell","description":null,"clone_url":"https://github.com/rkh/rbenv-whatis.git","ssh_url":"git@github.com:rkh/rbenv-whatis.git","git_url":"git://github.com/rkh/rbenv-whatis.git","created_at":"2012-02-22T13:52:44Z","id":3514978,"open_issues":0},{"html_url":"https://github.com/rkh/sinatra-template","pushed_at":"2012-03-14T15:07:40Z","updated_at":"2012-03-14T15:07:41Z","homepage":"","url":"https://api.github.com/repos/rkh/sinatra-template","has_downloads":true,"watchers":6,"fork":false,"svn_url":"https://github.com/rkh/sinatra-template","has_wiki":true,"has_issues":true,"size":124,"private":false,"mirror_url":null,"forks":5,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"sinatra-template","language":"Ruby","description":"base
36
+ template for classic sinatra apps","clone_url":"https://github.com/rkh/sinatra-template.git","ssh_url":"git@github.com:rkh/sinatra-template.git","git_url":"git://github.com/rkh/sinatra-template.git","created_at":"2012-02-25T13:39:12Z","id":3544708,"open_issues":0},{"html_url":"https://github.com/rkh/rbenv-update","pushed_at":"2012-02-25T14:17:43Z","updated_at":"2012-05-17T00:03:11Z","homepage":"","url":"https://api.github.com/repos/rkh/rbenv-update","has_downloads":true,"watchers":3,"fork":false,"svn_url":"https://github.com/rkh/rbenv-update","has_wiki":true,"has_issues":true,"size":84,"private":false,"mirror_url":null,"forks":2,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"rbenv-update","language":"Shell","description":"update
37
+ rbenv and plugins","clone_url":"https://github.com/rkh/rbenv-update.git","ssh_url":"git@github.com:rkh/rbenv-update.git","git_url":"git://github.com/rkh/rbenv-update.git","created_at":"2012-02-25T14:17:01Z","id":3544886,"open_issues":1},{"html_url":"https://github.com/rkh/call-for-proposals","pushed_at":"2012-03-06T09:42:48Z","updated_at":"2012-03-06T09:42:48Z","homepage":"","url":"https://api.github.com/repos/rkh/call-for-proposals","has_downloads":true,"watchers":1,"fork":true,"svn_url":"https://github.com/rkh/call-for-proposals","has_wiki":true,"has_issues":false,"size":160,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"call-for-proposals","language":null,"description":"Want
38
+ to make a talk proposal for EuRuKo 2012? This is the place to be!","clone_url":"https://github.com/rkh/call-for-proposals.git","ssh_url":"git@github.com:rkh/call-for-proposals.git","git_url":"git://github.com/rkh/call-for-proposals.git","created_at":"2012-02-29T14:04:31Z","id":3582162,"open_issues":0},{"html_url":"https://github.com/rkh/socialshareprivacy","pushed_at":"2012-03-01T11:20:47Z","updated_at":"2012-03-01T11:21:49Z","homepage":"","url":"https://api.github.com/repos/rkh/socialshareprivacy","has_downloads":true,"watchers":1,"fork":false,"svn_url":"https://github.com/rkh/socialshareprivacy","has_wiki":false,"has_issues":false,"size":216,"private":false,"mirror_url":null,"forks":1,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"socialshareprivacy","language":"JavaScript","description":"this
39
+ is just a mirror","clone_url":"https://github.com/rkh/socialshareprivacy.git","ssh_url":"git@github.com:rkh/socialshareprivacy.git","git_url":"git://github.com/rkh/socialshareprivacy.git","created_at":"2012-03-01T11:20:28Z","id":3591314,"open_issues":0},{"html_url":"https://github.com/rkh/Smallest-Federated-Wiki","pushed_at":"2012-03-01T21:36:10Z","updated_at":"2012-03-01T21:36:11Z","homepage":"http://wardcunningham.github.com/","url":"https://api.github.com/repos/rkh/Smallest-Federated-Wiki","has_downloads":true,"watchers":1,"fork":true,"svn_url":"https://github.com/rkh/Smallest-Federated-Wiki","has_wiki":true,"has_issues":false,"size":112,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"Smallest-Federated-Wiki","language":"JavaScript","description":"Our
40
+ new wiki innovates three ways. It shares through federation, composes by refactoring
41
+ and wraps data with visualization.","clone_url":"https://github.com/rkh/Smallest-Federated-Wiki.git","ssh_url":"git@github.com:rkh/Smallest-Federated-Wiki.git","git_url":"git://github.com/rkh/Smallest-Federated-Wiki.git","created_at":"2012-03-01T21:34:49Z","id":3596336,"open_issues":0},{"html_url":"https://github.com/rkh/gh","pushed_at":"2012-05-23T13:30:33Z","updated_at":"2012-05-23T13:30:35Z","homepage":"http://gh.rkh.im/","url":"https://api.github.com/repos/rkh/gh","has_downloads":true,"watchers":11,"fork":false,"svn_url":"https://github.com/rkh/gh","has_wiki":true,"has_issues":true,"size":244,"private":false,"mirror_url":null,"forks":3,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"gh","language":"Ruby","description":"Layered
42
+ GitHub API client","clone_url":"https://github.com/rkh/gh.git","ssh_url":"git@github.com:rkh/gh.git","git_url":"git://github.com/rkh/gh.git","created_at":"2012-03-05T13:07:41Z","id":3627076,"open_issues":0},{"html_url":"https://github.com/rkh/trinidad","pushed_at":"2012-03-10T15:56:37Z","updated_at":"2012-03-10T15:56:37Z","homepage":"","url":"https://api.github.com/repos/rkh/trinidad","has_downloads":true,"watchers":1,"fork":true,"svn_url":"https://github.com/rkh/trinidad","has_wiki":true,"has_issues":false,"size":152,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"trinidad","language":"Ruby","description":"Simple
43
+ library to run rails and rackup applications into an embedded Apache Tomcat","clone_url":"https://github.com/rkh/trinidad.git","ssh_url":"git@github.com:rkh/trinidad.git","git_url":"git://github.com/rkh/trinidad.git","created_at":"2012-03-10T15:55:36Z","id":3680426,"open_issues":0},{"html_url":"https://github.com/rkh/prefix","pushed_at":"2012-03-20T15:01:24Z","updated_at":"2012-03-20T15:21:14Z","homepage":null,"url":"https://api.github.com/repos/rkh/prefix","has_downloads":true,"watchers":2,"fork":false,"svn_url":"https://github.com/rkh/prefix","has_wiki":true,"has_issues":true,"size":92,"private":false,"mirror_url":null,"forks":1,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"prefix","language":"Ruby","description":null,"clone_url":"https://github.com/rkh/prefix.git","ssh_url":"git@github.com:rkh/prefix.git","git_url":"git://github.com/rkh/prefix.git","created_at":"2012-03-20T14:47:08Z","id":3776230,"open_issues":0},{"html_url":"https://github.com/rkh/hearts","pushed_at":"2012-03-25T16:45:47Z","updated_at":"2012-03-25T16:45:48Z","homepage":"","url":"https://api.github.com/repos/rkh/hearts","has_downloads":true,"watchers":1,"fork":true,"svn_url":"https://github.com/rkh/hearts","has_wiki":true,"has_issues":false,"size":108,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"hearts","language":"Ruby","description":"I
44
+ love hearts!","clone_url":"https://github.com/rkh/hearts.git","ssh_url":"git@github.com:rkh/hearts.git","git_url":"git://github.com/rkh/hearts.git","created_at":"2012-03-25T16:45:16Z","id":3825575,"open_issues":0},{"html_url":"https://github.com/rkh/test-project-matrix-1","pushed_at":"2012-04-15T16:21:18Z","updated_at":"2012-04-15T16:21:18Z","homepage":"","url":"https://api.github.com/repos/rkh/test-project-matrix-1","has_downloads":true,"watchers":1,"master_branch":"master","fork":true,"svn_url":"https://github.com/rkh/test-project-matrix-1","has_wiki":true,"has_issues":false,"size":112,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"test-project-matrix-1","language":"Ruby","description":"Test
45
+ dummy repository for testing Travis CI","clone_url":"https://github.com/rkh/test-project-matrix-1.git","ssh_url":"git@github.com:rkh/test-project-matrix-1.git","git_url":"git://github.com/rkh/test-project-matrix-1.git","created_at":"2012-04-15T16:20:55Z","id":4033173,"open_issues":0},{"html_url":"https://github.com/rkh/euruko-golf","pushed_at":"2012-05-04T11:54:45Z","updated_at":"2012-05-10T10:03:45Z","homepage":"","url":"https://api.github.com/repos/rkh/euruko-golf","has_downloads":true,"watchers":1,"fork":true,"svn_url":"https://github.com/rkh/euruko-golf","has_wiki":true,"has_issues":false,"size":108,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"euruko-golf","language":"Ruby","description":"Tweetable
46
+ ruby programs that output EuRuKo ASCII art.","clone_url":"https://github.com/rkh/euruko-golf.git","ssh_url":"git@github.com:rkh/euruko-golf.git","git_url":"git://github.com/rkh/euruko-golf.git","created_at":"2012-05-04T11:51:10Z","id":4224272,"open_issues":0},{"html_url":"https://github.com/rkh/github-services","pushed_at":"2012-05-15T12:00:57Z","updated_at":"2012-05-15T12:00:57Z","homepage":"http://github.com/blog/53-github-services-ipo","url":"https://api.github.com/repos/rkh/github-services","has_downloads":false,"watchers":1,"fork":true,"svn_url":"https://github.com/rkh/github-services","has_wiki":false,"has_issues":false,"size":120,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"github-services","language":"Ruby","description":"Official
47
+ GitHub Services Integration - You can set these up in your repo admin screen under
48
+ Service Hooks","clone_url":"https://github.com/rkh/github-services.git","ssh_url":"git@github.com:rkh/github-services.git","git_url":"git://github.com/rkh/github-services.git","created_at":"2012-05-15T11:35:46Z","id":4335034,"open_issues":0},{"html_url":"https://github.com/rkh/bundler","pushed_at":"2012-05-22T13:26:25Z","updated_at":"2012-05-22T13:26:25Z","homepage":"http://gembundler.com","url":"https://api.github.com/repos/rkh/bundler","has_downloads":false,"watchers":1,"master_branch":"master","fork":true,"svn_url":"https://github.com/rkh/bundler","has_wiki":true,"has_issues":false,"size":140,"private":false,"mirror_url":null,"forks":0,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"bundler","language":"Ruby","description":"Manage
49
+ your application''s gem dependencies with less pain","clone_url":"https://github.com/rkh/bundler.git","ssh_url":"git@github.com:rkh/bundler.git","git_url":"git://github.com/rkh/bundler.git","created_at":"2012-05-21T14:05:57Z","id":4394235,"open_issues":0},{"html_url":"https://github.com/rkh/gh-store","pushed_at":"2012-05-22T12:50:46Z","updated_at":"2012-05-22T13:11:14Z","homepage":null,"url":"https://api.github.com/repos/rkh/gh-store","has_downloads":true,"watchers":2,"fork":false,"svn_url":"https://github.com/rkh/gh-store","has_wiki":true,"has_issues":true,"size":92,"private":false,"mirror_url":null,"forks":1,"owner":{"url":"https://api.github.com/users/rkh","login":"rkh","avatar_url":"https://secure.gravatar.com/avatar/5c2b452f6eea4a6d84c105ebd971d2a4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"5c2b452f6eea4a6d84c105ebd971d2a4","id":30442},"name":"gh-store","language":"Ruby","description":null,"clone_url":"https://github.com/rkh/gh-store.git","ssh_url":"git@github.com:rkh/gh-store.git","git_url":"git://github.com/rkh/gh-store.git","created_at":"2012-05-22T12:50:34Z","id":4406633,"open_issues":0}]'