git_hub_bub 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 833f07fd265f877003b7f27782bd3c0b40b63cfe
4
+ data.tar.gz: 4e63581207216c9c6147a384f7ea411c3432fd04
5
+ SHA512:
6
+ metadata.gz: 78424f9c9e28d47175c5c7ae70a9f5ac247be3bec1659cd7e33a3393e2167cc6a55675f81d14a891492564a48ddb48ff6037703a54a8c1e0bc8980a921a59583
7
+ data.tar.gz: 73fe92e332b1098962208a9ebe487855981c2cbeddea6f003208f0e5b6d128fc7f4d4b65dc8da2b08bf13d903e1c80a140905ed430837dacca8f3ea53042edd2
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .env
2
+ .DS_Store
3
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - ruby-head
6
+ - jruby-19mode
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # GitHubBub
2
+
3
+ A low level GitHub client that makes the disgusting issue of header based url pagination simple.
4
+
5
+ ## What
6
+
7
+ Seriously? I just told you, see above.
8
+
9
+ ## Why
10
+
11
+ I'm using this in a few places such as http://www.codetriage.com/. Need low level control, without sacrificing usability.
12
+
13
+ ## Install
14
+
15
+ In your `Gemfile`
16
+
17
+ ```
18
+ gem 'git_hub_bub'
19
+ ```
20
+
21
+ Then run `$ bundle install`
22
+
23
+ ## GET Whatever you Want:
24
+
25
+ To make requests to a `GET` endpoint use `GitHubBub.get`
26
+
27
+ ```ruby
28
+ response = GitHubBub.get('repos/rails/rails/issues')
29
+ ```
30
+
31
+ Now you can do stuff like grab the json-ified body:
32
+
33
+ ```ruby
34
+ response.json_body # => { foo: "bar" ...}
35
+ ```
36
+
37
+ And get pagination (if there is any):
38
+
39
+ ```ruby
40
+ response.next_url # => "https://api.github.com/repositories/8514/issues?page=2"
41
+ response.last_url? # => false
42
+ response.pagination # => {"next_url"=>"https://api.github.com/repositories/8514/issues?page=2", "last_url"=>"https://api.github.com/repositories/8514/issues?page=18"}
43
+ ```
44
+
45
+ ## Passing Params
46
+
47
+ To pass parameters such as page number, or sorting or whatever, input a hash as the second argument.
48
+
49
+ ```ruby
50
+ GitHubBub.get('repositories/8514/issues', page: 1, sort: 'comments', direction:'desc')
51
+ ```
52
+
53
+ ## Passing Anything Else
54
+
55
+ Anything else you pass in the third argument will be given to [Excon](https://github.com/geemus/excon) which powers GitHubBub. So if you want to set headers you can do it like this:
56
+
57
+ ```ruby
58
+ GitHubBub.get('repositories/8514/issues', {page: 1}, {headers: { "Content-Type" => "application/x-www-form-urlencoded" }})
59
+ ```
60
+
61
+ or
62
+
63
+ ```ruby
64
+ GitHubBub.get('repositories/8514/issues', {}, {headers: { "Content-Type" => "application/x-www-form-urlencoded" }})
65
+ ```
66
+
67
+ See [Excon](https://github.com/geemus/excon) for documentation on more available options.
68
+
69
+ ## Default Headers
70
+
71
+ Default headers are set in `GitHubBub::Request`
72
+
73
+ ```ruby
74
+ BASE_HEADERS = {'Accept' => "application/#{GITHUB_VERSION}", "User-Agent" => USER_AGENT}
75
+ ```
76
+
77
+ You can change `GitHubBub::Request::GITHUB_VERSION` and `GitHubBub::Request::USER_AGENT`.
78
+
79
+ If you want any other default headers you can set them in `EXTRA_HEADERS` like so:
80
+
81
+ ```ruby
82
+ GitHubBub::Request::EXTRA_HEADERS = { "Content-Type" => "application/x-www-form-urlencoded" }
83
+ ```
84
+
85
+ Keep in mind this will change them for _every_ request. If you need logic behind your default headers, consider adding a `before_send_callback` to conditionally modify headers
86
+
87
+
88
+ ## Authenticated Requests
89
+
90
+ Some GitHub endpoints require a user's authorization you can do that by passing in `token`:
91
+
92
+ ```ruby
93
+ GitHubBub.get('/user', token: 'a38ck38ckgoldfishtoken')
94
+ ```
95
+
96
+ Or you can manually set a header like so:
97
+
98
+ ```ruby
99
+ GitHubBub.get('/user', {} {headers: {"Authorization" => "token a38ck38ckgoldfishtoken"}})
100
+ ```
101
+
102
+ You will need to use one of these every time the GitHub api says "as an authenticated user".
103
+
104
+ ## Callbacks
105
+
106
+ If you want to mess with the url or options before sending a request you can set a callback globally
107
+
108
+ ```ruby
109
+ GitHubBub::Request.set_callback do |request|
110
+ request.url = "http://schneems.com"
111
+ request.options = {do: "anything you want to _all_ the requests" }
112
+ end
113
+ ```
114
+
115
+ ## Endpoints
116
+
117
+ Check [GitHub Developer Docs](http://developer.github.com/). When you see something like
118
+
119
+ ```
120
+ GET /users/:user/repos
121
+ ```
122
+
123
+ It means you need to use the `GitHubBub.get` method and pass in a string like `'/users/schneems/repos'` the full request might look like this:
124
+
125
+
126
+ ```ruby
127
+ GitHubBub.get('/users/schneems/repos')
128
+ ```
129
+
130
+ ## Other HTTP Methods
131
+
132
+ Supports everything GitHub currently supports http://developer.github.com/v3/#http-verbs :
133
+
134
+ ```
135
+ HEAD # => GitHubBub.head
136
+ GET # => GitHubBub.get
137
+ POST # => GitHubBub.post
138
+ PATCH # => GitHubBub.patch
139
+ PUT # => GitHubBub.put
140
+ DELETE # => GitHubBub.delete
141
+ ```
142
+
143
+
144
+ ## Configuration
145
+
146
+ You can use callbacks and there are some constants you can set, look in `GitHubBub::Request`. You will definetly want to set `GitHubBub::Request::USER_AGENT` It needs to be unique to your app: (http://developer.github.com/v3/#user-agent-required).
147
+
148
+ ```ruby
149
+ GitHubBub::Request::USER_AGENT = 'a-unique-and-permanent-agent-to-my-app'
150
+ ```
151
+
152
+ ## Testing
153
+
154
+ This gem is tested using the super cool request recording/stubbing framework [VCR](https://github.com/vcr/vcr). This does mean at one point and time all tests ran successfully against GitHub's servers. This also means if you want to write any tests any that are not already recorded will need to hit GitHub servers. So make sure the tests you write don't do anything really bad.
155
+
156
+ You'll also need a valid `.env` file
157
+
158
+ ```sh
159
+ $ touch .env
160
+ ```
161
+
162
+ Anything you put in this file will be sourced into your environment for tests. Here is an example `.env` file.
163
+
164
+ ```sh
165
+ GITHUB_API_KEY=asdfe92fakeKey43ad638e35asdfd98167847248a26
166
+ OWNER=schneems
167
+ REPO=wicked
168
+ USER_NAME="Richard Schneeman"
169
+ WATCH_OWNER=emberjs
170
+ WATCH_REPO=ember.js
171
+ ```
172
+
173
+ You will need to change most of these values
174
+
175
+ ```
176
+ GITHUB_API_KEY=asdfe92fakeKey43ad638e35asdfd98167847248a26
177
+ ```
178
+
179
+ Your github API key, you can get one from https://github.com/settings/applications.
180
+
181
+ ```
182
+ OWNER=schneems
183
+ ```
184
+
185
+ Your public github username
186
+
187
+ ```
188
+ REPO=wicked
189
+ ```
190
+
191
+ A repo that you have commit access too.
192
+
193
+ ```
194
+ USER_NAME="Richard Schneeman"
195
+ ```
196
+
197
+ Your real name
198
+
199
+ ```
200
+ WATCH_OWNER=emberjs
201
+ ```
202
+
203
+ The `:owner` of a repo you might watch or want to watch, needs to be combined with WATCH_REPO. This should be different from `OWNER`
204
+
205
+ ```
206
+ WATCH_REPO=ember.js
207
+ ```
208
+
209
+ A repo that the `WATCH_OWNER` owns. Should be different from `REPO`
210
+
211
+
212
+ ## License
213
+
214
+ MIT
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'git_hub_bub'
3
+
4
+ task :default => [:test]
5
+
6
+ require 'rake'
7
+ require 'rake/testtask'
8
+
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
data/changelog.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.0.1 (05/21/2012)
2
+
3
+ - Hello World
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'git_hub_bub/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "git_hub_bub"
8
+ gem.version = GitHubBub::VERSION
9
+ gem.authors = ["Richard Schneeman"]
10
+ gem.email = ["richard.schneeman+rubygems@gmail.com"]
11
+ gem.description = %q{git_hub_bub makes github requests}
12
+ gem.summary = %q{git_hub_bub makes github requests}
13
+ gem.homepage = "https://github.com/schneems/git_hub_bub"
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_dependency "rrrretry"
22
+ gem.add_dependency "excon"
23
+ gem.add_development_dependency "rake"
24
+ gem.add_development_dependency "vcr", '~> 2.5.0'
25
+ gem.add_development_dependency "webmock", '~> 1.11.0'
26
+ gem.add_development_dependency "dotenv"
27
+ end
28
+
@@ -0,0 +1,137 @@
1
+ module GitHubBub
2
+ class RequestError < StandardError; end
3
+
4
+ class Request
5
+ attr_accessor :url, :options, :token
6
+ BASE_URI = 'https://api.github.com'
7
+ USER_AGENT ||= SecureRandom.hex(16)
8
+ GITHUB_VERSION = "vnd.github.3.raw+json"
9
+ EXTRA_HEADERS ||= {}
10
+ BASE_HEADERS = EXTRA_HEADERS.merge({'Accept' => "application/#{GITHUB_VERSION}", "User-Agent" => USER_AGENT})
11
+ RETRIES = 1
12
+
13
+ def initialize(url, query = {}, options = {})
14
+ self.url = url.include?("http") ? url : File.join(BASE_URI, url)
15
+ self.options = options || {}
16
+ self.options[:query] = query if query && !query.empty?
17
+ options[:headers] = BASE_HEADERS.merge(options[:headers]|| {})
18
+ end
19
+
20
+ def self.head(url, query = {}, options = {})
21
+ self.new(url, query, options).head
22
+ end
23
+
24
+ def head
25
+ wrap_request do
26
+ Excon.head(url, options)
27
+ end
28
+ end
29
+
30
+ def self.get(url, query = {}, options = {})
31
+ self.new(url, query, options).get
32
+ end
33
+
34
+ def get
35
+ wrap_request do
36
+ ex = Excon.get(url, options)
37
+ ex = Excon.get(@location, options) if @location = ex.headers["Location"]
38
+ ex
39
+ end
40
+ end
41
+
42
+ def self.post(url, query = {}, options = {})
43
+ self.new(url, query, options).post
44
+ end
45
+
46
+ def post
47
+ wrap_request do
48
+ Excon.post(url, options)
49
+ end
50
+ end
51
+
52
+ def self.patch(url, query = {}, options = {})
53
+ self.new(url, query, options).patch
54
+ end
55
+
56
+ def patch
57
+ wrap_request do
58
+ Excon.patch(url, options)
59
+ end
60
+ end
61
+
62
+ def self.put(url, query = {}, options = {})
63
+ self.new(url, query, options).put
64
+ end
65
+
66
+ def put
67
+ wrap_request do
68
+ Excon.put(url, options)
69
+ end
70
+ end
71
+
72
+ def self.delete(url, query = {}, options = {})
73
+ self.new(url, query, options).delete
74
+ end
75
+
76
+ def delete
77
+ wrap_request do
78
+ Excon.delete(url, options)
79
+ end
80
+ end
81
+
82
+ def wrap_request(&block)
83
+ before_callbacks!
84
+ set_auth_from_token!
85
+ query_to_json_body!
86
+ response = RETRIES.times.retry do
87
+ GitHubBub::Response.create(yield)
88
+ end
89
+ raise RequestError, "message: '#{response.json_body['message']}', url: '#{url}', response: '#{response.inspect}'" unless response.status.to_s =~ /^2.*/
90
+ return response
91
+ end
92
+
93
+ # do they take query params? do they take :body?
94
+ # who cares, send them both!
95
+ def query_to_json_body!
96
+ options[:body] = options[:query].to_json if options[:query]
97
+ end
98
+
99
+ def set_auth_from_token!
100
+ return unless token
101
+ options[:headers]["Authorization"] ||= "token #{token}"
102
+ end
103
+
104
+ def token
105
+ @token ||= if options[:headers] && token_string = options[:headers]["Authorization"]
106
+ token_string.split(/\s/).last
107
+ elsif options[:query] && token = options[:query].delete(:token)
108
+ token
109
+ else
110
+ nil
111
+ end
112
+ end
113
+ alias :token? :token
114
+
115
+ def self.set_before_callback(&block)
116
+ before_callbacks << block
117
+ end
118
+
119
+ def self.before_callbacks
120
+ @before_callbacks ||=[]
121
+ end
122
+
123
+ def self.clear_callbacks
124
+ @before_callbacks = []
125
+ end
126
+
127
+ def before_callbacks!
128
+ self.class.before_callbacks.each do |callback|
129
+ run_callback &callback
130
+ end
131
+ end
132
+
133
+ def run_callback(&block)
134
+ yield self
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,66 @@
1
+ module GitHubBub
2
+ class Response < Excon::Response
3
+
4
+ def self.create(response)
5
+ self.new(response.data)
6
+ end
7
+
8
+ def json_body
9
+ ::JSON.parse(self.body)
10
+ end
11
+
12
+ def pagination
13
+ @pagination ||= parse_pagination
14
+ end
15
+
16
+ def parsed_response
17
+ response.body.inspect
18
+ end
19
+
20
+ def next_url
21
+ pagination['next_url']
22
+ end
23
+
24
+ def prev_url
25
+ pagination['prev_url']
26
+ end
27
+ alias :previous_url :prev_url
28
+
29
+ def last_url
30
+ pagination['last_url']
31
+ end
32
+
33
+ def first_url
34
+ pagination['first_url']
35
+ end
36
+
37
+ def last_page?
38
+ return true if next_url.nil?
39
+ last_page_number = page_number_from_url(last_url)
40
+ next_page_number = page_number_from_url(next_url)
41
+ return next_page_number > last_page_number
42
+ end
43
+
44
+ def first_page?
45
+ return true if first_url.nil?
46
+ return false
47
+ end
48
+
49
+ def page_number_from_url(url)
50
+ query = ::URI.parse(url).query
51
+ ::CGI.parse(query)["page"].first.to_i
52
+ end
53
+
54
+ def header_links
55
+ (headers['link'] || headers['Link'] || "").split(',')
56
+ end
57
+
58
+ def parse_pagination
59
+ header_links.each_with_object({}) do |element, hash|
60
+ key = element[/rel=["'](.*)['"]/, 1]
61
+ value = element[/<(.*)>/, 1]
62
+ hash["#{key}_url"] = value
63
+ end
64
+ end
65
+ end
66
+ end