octocat_herder 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document CHANGED
@@ -2,4 +2,5 @@ lib/**/*.rb
2
2
  bin/*
3
3
  -
4
4
  features/**/*.feature
5
- LICENSE.txt
5
+ LICENSE
6
+ CONTRIBUTING.markdown
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
@@ -0,0 +1,93 @@
1
+ Checklist (and a short version for the impatient)
2
+ =================================================
3
+
4
+ * Commits:
5
+
6
+ - Make commits of logical units.
7
+
8
+ - Check for unnecessary whitespace with "git diff --check" before
9
+ committing.
10
+
11
+ - Commit using Unix line endings (check the settings around "crlf"
12
+ in git-config(1)).
13
+
14
+ - Do not check in commented out code or unneeded files.
15
+
16
+ - The first line of the commit message should be a short
17
+ description (50 characters is the soft limit, excluding ticket
18
+ number(s)), and should skip the full stop.
19
+
20
+ - The body should provide a meaningful commit message, which:
21
+
22
+ - uses the imperative, present tense: "change", not "changed" or
23
+ "changes".
24
+
25
+ - includes motivation for the change, and contrasts its
26
+ implementation with the previous behavior.
27
+
28
+ - Make sure that you have tests for the bug you are fixing, or
29
+ feature you are adding.
30
+
31
+ - Make sure the test suite passes after your commit (rake spec).
32
+
33
+ * Submission:
34
+
35
+ - Fork the repository on GitHub.
36
+
37
+ - Push your changes to a topic branch in your fork of the
38
+ repository.
39
+
40
+ - Submit a pull request.
41
+
42
+ The long version
43
+ ================
44
+
45
+ 1. Make separate commits for logically separate changes.
46
+
47
+ Please break your commits down into logically consistent units
48
+ which include new or changed tests relevent to the rest of the
49
+ change. The goal of doing this is to make the diff easier to
50
+ read for whoever is reviewing your code. In general, the easier
51
+ your diff is to read, the more likely someone will be happy to
52
+ review it and get it into the code base.
53
+
54
+ If you're going to refactor a piece of code, please do so as a
55
+ separate commit from your feature or bug fix changes.
56
+
57
+ We also really appreciate changes that include tests to make
58
+ sure the bug isn't re-introduced, and that the feature isn't
59
+ accidentally broken.
60
+
61
+ Describe the technical detail of the change(s). If your
62
+ description starts to get too long, that's a good sign that you
63
+ probably need to split up your commit into more finely grained
64
+ pieces.
65
+
66
+ Commits which plainly describe the the things which help
67
+ reviewers check the patch and future developers understand the
68
+ code are much more likely to be merged in with a minimum of
69
+ bike-shedding or requested changes. Ideally, the commit message
70
+ would include information, and be in a form suitable for
71
+ inclusion in the release notes.
72
+
73
+ Please also check that you are not introducing any trailing
74
+ whitespaces or other "whitespace errors". You can do this by
75
+ running "git diff --check" on your changes before you commit.
76
+
77
+ 3. Sending your changes
78
+
79
+ To submit your changes via a GitHub pull request, we _highly_
80
+ recommend that you have them on a topic branch, instead of
81
+ directly on "master". It makes things much easier to keep track
82
+ of, especially if you decide to work on another thing before
83
+ your first change is merged in.
84
+
85
+ GitHub has some pretty good
86
+ [general documentation](http://help.github.com/) on using
87
+ their site. They also have documentation on
88
+ [creating pull requests](http://help.github.com/send-pull-requests/).
89
+
90
+ In general, after pushing your topic branch up to your
91
+ repository on GitHub, you'll switch to the branch in the GitHub
92
+ UI and click "Pull Request" towards the top of the page in order
93
+ to open a pull request.
data/Gemfile CHANGED
@@ -4,10 +4,12 @@ gem "httparty", "~> 0.7.8"
4
4
  gem "link_header", "~> 0.0.5"
5
5
 
6
6
  group :development do
7
- gem "rspec", "~> 2.6.0"
8
- gem "mocha", "~> 0.9.12"
9
- gem "bundler", "~> 1.0.0"
10
- gem "jeweler", "~> 1.6.4"
11
- gem "rcov", ">= 0"
12
- gem "rdoc", ">= 2.4.2"
7
+ gem "rspec", "~> 2.6.0"
8
+ gem "mocha", "~> 0.9.12"
9
+ gem "bundler", "~> 1.0.0"
10
+ gem "jeweler", "~> 1.6.4"
11
+ gem "rcov", ">= 0"
12
+ gem "yard", "~> 0.6.0"
13
+ gem "rdoc", "~> 3.8.0"
14
+ gem "bluecloth", "~>2.1.0"
13
15
  end
data/Gemfile.lock CHANGED
@@ -1,6 +1,7 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ bluecloth (2.1.0)
4
5
  crack (0.1.8)
5
6
  diff-lcs (1.1.2)
6
7
  git (1.2.5)
@@ -23,16 +24,19 @@ GEM
23
24
  rspec-expectations (2.6.0)
24
25
  diff-lcs (~> 1.1.2)
25
26
  rspec-mocks (2.6.0)
27
+ yard (0.6.8)
26
28
 
27
29
  PLATFORMS
28
30
  ruby
29
31
 
30
32
  DEPENDENCIES
33
+ bluecloth (~> 2.1.0)
31
34
  bundler (~> 1.0.0)
32
35
  httparty (~> 0.7.8)
33
36
  jeweler (~> 1.6.4)
34
37
  link_header (~> 0.0.5)
35
38
  mocha (~> 0.9.12)
36
39
  rcov
37
- rdoc (>= 2.4.2)
40
+ rdoc (~> 3.8.0)
38
41
  rspec (~> 2.6.0)
42
+ yard (~> 0.6.0)
File without changes
data/README.markdown ADDED
@@ -0,0 +1,46 @@
1
+ # Octocat Herder [![Build status](http://travis-ci.org/jhelwig/octocat_herder.png)](http://travis-ci.org/jhelwig/octocat_herder)
2
+
3
+ Octocat Herder is a wrapper for the GitHub v3 API.
4
+
5
+ ## On-line documentation
6
+
7
+ * [rdoc.info](http://rdoc.info/gems/octocat_herder/frames)
8
+
9
+ ## Requirements
10
+
11
+ * Gems
12
+ * link_header
13
+ * httparty
14
+
15
+ ## Basic usage
16
+
17
+ Quick start:
18
+
19
+ require 'rubygems'
20
+ => true
21
+ require 'octocat_herder'
22
+ => true
23
+
24
+ herder = OctocatHerder.new
25
+ => #<OctocatHerder:0x7f792d3c4918 ...>
26
+
27
+ me = herder.user 'jhelwig'
28
+ => #<OctocatHerder::User:0x7f792d3b6070 ...>
29
+
30
+ me.html_url
31
+ => "https://github.com/jhelwig"
32
+
33
+ me.available_attributes.sort
34
+ => ["avatar_url", "bio", "blog", "company", "created_at", "email", "followers", "following", "hireable", "html_url", "location", "login", "name", "public_gists", "public_repos", "url", "user_id", "user_type"]
35
+
36
+ repos = me.repositories
37
+ => [#<OctocatHerder::Repository:0x7f792d364bf8 ...>, #<OctocatHerder::Repository:0x7f792d364bd0 ..>, #<OctocatHerder::Repository:0x7f792d364ba8 ...>, ...]
38
+
39
+
40
+ ## Contributing to octocat_herder
41
+
42
+ See {file:CONTRIBUTING.markdown CONTRIBUTING.markdown} for further details.
43
+
44
+ ## Copyright
45
+
46
+ Copyright (c) 2011 Jacob Helwig. See {file:LICENSE LICENSE} for further details.
data/Rakefile CHANGED
@@ -38,12 +38,5 @@ end
38
38
 
39
39
  task :default => :spec
40
40
 
41
- require 'rdoc/task'
42
- Rake::RDocTask.new do |rdoc|
43
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
-
45
- rdoc.rdoc_dir = 'rdoc'
46
- rdoc.title = "octocat_herder #{version}"
47
- rdoc.rdoc_files.include('README*')
48
- rdoc.rdoc_files.include('lib/**/*.rb')
49
- end
41
+ require 'yard'
42
+ YARD::Rake::YardocTask.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.1.0
@@ -1,19 +1,50 @@
1
1
  require 'cgi'
2
2
  require 'link_header'
3
- require 'parsedate'
3
+
4
+ begin
5
+ require 'parsedate'
6
+ rescue LoadError
7
+ end
8
+
4
9
  require 'uri'
5
10
 
6
11
  require 'octocat_herder/connection'
7
12
 
8
13
  class OctocatHerder
9
- class Base
10
- attr_reader :raw, :connection
11
-
14
+ # This provides most of the functionality to interact with the
15
+ # GitHub v3 API.
16
+ module Base
17
+ # The re-hydrated JSON retrieved from the GitHub API.
18
+ #
19
+ # @since 0.0.1
20
+ # @return [Hash]
21
+ attr_reader :raw
22
+
23
+ # Our {OctocatHerder::Connection}, so we can make more requests
24
+ # based on the information we retrieved from the GitHub API.
25
+ #
26
+ # @since 0.0.1
27
+ # @return [OctocatHerder::Connection]
28
+ attr_reader :connection
29
+
30
+ # @api private
31
+ # @since 0.0.1
32
+ #
33
+ # @param [Hash] raw_hash The re-hydrated JSON received from the
34
+ # GitHub API via {OctocatHerder::Connection}.
35
+ #
36
+ # @param [OctocatHerder::Connection] conn If not provided requests
37
+ # will be unauthenticated.
12
38
  def initialize(raw_hash, conn = OctocatHerder::Connection.new)
13
39
  @connection = conn
14
40
  @raw = raw_hash
15
41
  end
16
42
 
43
+ # We use the +method_missing+ magic to create accessors for the
44
+ # information we got back from the GitHub API. You can get a list
45
+ # of all of the available things from {#available_attributes}.
46
+ #
47
+ # @since 0.0.1
17
48
  def method_missing(id, *args)
18
49
  unless @raw and @raw.keys.include?(id.id2name)
19
50
  raise NoMethodError.new("undefined method #{id.id2name} for #{self}:#{self.class}")
@@ -22,6 +53,13 @@ class OctocatHerder
22
53
  @raw[id.id2name]
23
54
  end
24
55
 
56
+ # This returns a list of the things that the API request returned
57
+ # to us.
58
+ #
59
+ # @since 0.0.1
60
+ #
61
+ # @return [Array<String>] Names of available methods providing
62
+ # additional detail about the object.
25
63
  def available_attributes
26
64
  attrs = []
27
65
  attrs += @raw.keys.reject do |k|
@@ -36,53 +74,24 @@ class OctocatHerder
36
74
 
37
75
  private
38
76
 
39
- def self.raw_get(conn, end_point, options={})
40
- paginated = options.delete(:paginated)
41
- query_params = options.delete(:params) || {}
42
-
43
- query_params[:per_page] = 100 if paginated and query_params[:per_page].nil?
44
- query_string = query_string_from_params(query_params)
45
-
46
- result = conn.get(end_point + query_string, options)
47
- raise "Unable to retrieve #{end_point}" unless result
48
-
49
- full_result = result.parsed_response
50
-
51
- if paginated
52
- if next_page = page_from_headers(result.headers, 'next')
53
- query_params[:page] = next_page
54
-
55
- new_options = options.merge(query_params)
56
- new_options[:paginated] = true
57
-
58
- full_result += raw_get(conn, end_point, new_options)
59
- end
60
- end
61
-
62
- full_result
63
- end
64
-
65
- def self.page_from_headers(headers, type)
66
- link = LinkHeader.parse(headers['link']).find_link(['rel', type])
67
- return unless link
68
-
69
- CGI.parse(URI.parse(link.href).query)['page'].first
70
- end
71
-
72
- def self.query_string_from_params(params)
73
- return '' if params.keys.empty?
74
-
75
- '?' + params.map {|k,v| "#{URI.escape("#{k}")}=#{URI.escape("#{v}")}"}.join('&')
76
- end
77
-
77
+ # Intended to be overridden in classes using {OctocatHerder::Base},
78
+ # so they can make the methods they define show up in
79
+ # {#available_attributes}.
80
+ #
81
+ # @since 0.0.1
78
82
  def additional_attributes
79
83
  []
80
84
  end
81
85
 
86
+ # @since 0.0.1
82
87
  def parse_date_time(date_time)
83
88
  return nil unless date_time
84
89
 
85
- Time.utc(*ParseDate.parsedate(date_time))
90
+ if defined? ParseDate
91
+ Time.utc(*ParseDate.parsedate(date_time))
92
+ else
93
+ DateTime.parse date_time
94
+ end
86
95
  end
87
96
  end
88
97
  end
@@ -2,16 +2,48 @@ require 'httparty'
2
2
  require 'link_header'
3
3
 
4
4
  class OctocatHerder
5
+ # This implements some additional functionality around HTTParty to
6
+ # help make working with the GitHub API a little nicer.
5
7
  class Connection
6
8
  include HTTParty
7
9
  base_uri 'https://api.github.com'
8
10
 
9
- attr_reader :user_name, :password, :oauth2_token
11
+ # User name to use when doing basic HTTP authentication.
12
+ #
13
+ # @since 0.0.1
14
+ attr_reader :user_name
10
15
 
11
- def httparty_options
12
- @httparty_options || {}
13
- end
16
+ # Password to use when doing basic HTTP authentication.
17
+ #
18
+ # @since 0.0.1
19
+ attr_reader :password
20
+
21
+ # The OAuth2 token to use when doing OAuth2 authentication.
22
+ #
23
+ # @since 0.0.1
24
+ attr_reader :oauth2_token
14
25
 
26
+ # If provided a hash of login information, the
27
+ # {OctocatHerder::Connection} will attempt to make authenticated
28
+ # requests.
29
+ #
30
+ # @example Unauthenticated requests
31
+ # connection = OctocatHerder::Connection.new
32
+ #
33
+ # @example Providing an OAuth2 token
34
+ # connection = OctocatHerder::Connection.new :oauth2_token => 'token'
35
+ #
36
+ # @example Providing user name & password
37
+ # connection = OctocatHerder::Connection.new :user_name => 'user', :password => 'pass'
38
+ #
39
+ # If no hash is provided, then unauthenticated requests will be
40
+ # made.
41
+ #
42
+ # @since 0.0.1
43
+ # @param [Hash<Symbol => String>] options Login information
44
+ # @option options [String] :user_name
45
+ # @option options [String] :password
46
+ # @option options [String] :oauth2_token
15
47
  def initialize(options={})
16
48
  raise ArgumentError.new(
17
49
  "OctocatHerder::Connection does not accept: #{options.class}"
@@ -29,7 +61,7 @@ class OctocatHerder
29
61
  end
30
62
 
31
63
  if options.keys.include?(:oauth2_token) and options.keys.include?(:user_name)
32
- raise ArgumentError.new('Cannot provide both an OAuth2 Token, and a user name and password')
64
+ raise ArgumentError.new('Cannot provide both an OAuth2 token, and a user name and password')
33
65
  end
34
66
 
35
67
  @user_name = options[:user_name]
@@ -43,13 +75,120 @@ class OctocatHerder
43
75
  end
44
76
  end
45
77
 
78
+ # Execute a GET request against the GitHub v3 API.
79
+ #
80
+ # @since development
81
+ #
82
+ # @param [String] end_point The part of the API URL after
83
+ # +'api.github.com'+, including the leading +'/'+.
84
+ #
85
+ # @param [Hash] options A Hash of options to be passed down to
86
+ # HTTParty, with a couple of extra options.
87
+ #
88
+ # @option options [true, false] :paginated Retrieve all pages from
89
+ # a paginated end-point.
90
+ #
91
+ # @option options [Hash<String, Symbol => String>] :params
92
+ # Constructed into a query string using
93
+ # {OctocatHerder::Connection#query_string_from_params}.
46
94
  def get(end_point, options={})
95
+ paginated = options.delete(:paginated)
96
+ options[:params] ||= {}
97
+
98
+ options[:params][:per_page] = 100 if paginated and options[:params][:per_page].nil?
99
+
100
+ result = raw_get(end_point, options)
101
+ raise "Unable to retrieve #{end_point}" unless result
102
+
103
+ full_result = result.parsed_response
104
+
105
+ if paginated
106
+ if next_page = page_from_headers(result.headers, 'next')
107
+ options[:params][:page] = next_page
108
+ options[:paginated] = true
109
+
110
+ full_result += raw_get(end_point, options)
111
+ end
112
+ end
113
+
114
+ full_result
115
+ end
116
+
117
+ # Small wrapper around HTTParty.get, which handles adding
118
+ # authentication information to the API request.
119
+ #
120
+ # @since development
121
+ def raw_get(end_point, options={})
122
+ query_params = options.delete(:params) || {}
123
+ query_string = query_string_from_params(query_params)
124
+
47
125
  request_options = options.merge(httparty_options)
48
126
  if httparty_options.has_key?(:headers) and options.has_key(:headers)
49
127
  request_options[:headers] = options[:headers].merge(httparty_options[:headers])
50
128
  end
51
129
 
52
- OctocatHerder::Connection.get(end_point, request_options)
130
+ OctocatHerder::Connection.get(end_point + query_string, request_options)
131
+ end
132
+
133
+ # Are we making authenticated requests?
134
+ #
135
+ # @since development
136
+ # @return [true, false]
137
+ def authenticated_requests?
138
+ if (user_name and password) or oauth2_token
139
+ true
140
+ else
141
+ false
142
+ end
143
+ end
144
+
145
+ # Retrieve the page number of a given 'Link:' header from a hash
146
+ # of HTTP Headers
147
+ #
148
+ # +type+ can be one of:
149
+ # ['+next+'] The immediate next page of results.
150
+ # ['+last+'] The last page of first.
151
+ # ['+first+'] The first page of results.
152
+ # ['+prev+'] The immediate previous page of results.
153
+ #
154
+ # @since development
155
+ #
156
+ # @raise [ArgumentError] If type is not one of the allowed values.
157
+ #
158
+ # @param [Hash] headers
159
+ #
160
+ # @param ['next', 'last', 'first', 'prev'] type
161
+ def page_from_headers(headers, type)
162
+ raise ArgumentError.new(
163
+ "Unknown type: #{type}"
164
+ ) unless ['next', 'last', 'first', 'prev'].include? type
165
+
166
+ link = LinkHeader.parse(headers['link']).find_link(['rel', type])
167
+ return unless link
168
+
169
+ CGI.parse(URI.parse(link.href).query)['page'].first
170
+ end
171
+
172
+ # Convenience method to generate URL query strings.
173
+ #
174
+ # @since development
175
+ #
176
+ # @param [Hash] params A Hash of key/values to be turned into a
177
+ # URL query string. Does not support nested data.
178
+ #
179
+ # @return [String] Empty string if params is an empty hash,
180
+ # otherwise a string of the query parameters with a leading
181
+ # +'?'+.
182
+ def query_string_from_params(params)
183
+ return '' if params.keys.empty?
184
+
185
+ '?' + params.map {|k,v| "#{URI.escape("#{k}")}=#{URI.escape("#{v}")}"}.join('&')
186
+ end
187
+
188
+ private
189
+
190
+ def httparty_options
191
+ @httparty_options || {}
53
192
  end
54
193
  end
55
194
  end
@@ -3,32 +3,66 @@ require 'octocat_herder/user'
3
3
  require 'octocat_herder/repository'
4
4
 
5
5
  class OctocatHerder
6
- class PullRequest < Base
7
- class Repo < ::OctocatHerder::Base
6
+ class PullRequest
7
+ # A representation of the repsoitory meta-data returned from the
8
+ # pull request API. This is only ever useful when returned from
9
+ # OctocatHerder::PullRequest#head or
10
+ # OctocatHerder::PullRequest#base.
11
+ class Repo
12
+ include OctocatHerder::Base
13
+
14
+ # The login name of the owner of this repository.
15
+ #
16
+ # @since 0.0.1
17
+ # @return [String]
8
18
  def user_login
9
19
  @raw['user']['login']
10
20
  end
11
21
 
22
+ # The ID number of the owner of this repository.
23
+ #
24
+ # @since 0.0.1
25
+ # @return [Integer]
12
26
  def user_id
13
27
  @raw['user']['id']
14
28
  end
15
29
 
30
+ # The URL to the avatar image of the owner of this repository.
31
+ #
32
+ # @since 0.0.1
33
+ # @return [String]
16
34
  def user_avatar_url
17
35
  @raw['user']['avatar_url']
18
36
  end
19
37
 
38
+ # The URL of the owner of this repository.
39
+ #
40
+ # @since 0.0.1
41
+ # @return [String]
20
42
  def user_url
21
43
  @raw['user']['url']
22
44
  end
23
45
 
46
+ # The owner of this repository.
47
+ #
48
+ # @note This is cached locally to the instance of OctocatHerder::PullRequest::Repo, but will make an additional API request to populate it initially.
49
+ #
50
+ # @since 0.0.1
51
+ # @return [OctocatHerder::User]
24
52
  def user
25
- @user = OctocatHerder::User.fetch(@raw['user'], connection)
53
+ @user ||= OctocatHerder::User.fetch(@raw['user'], connection)
26
54
  end
27
55
 
56
+ # The detailed information about the repository.
57
+ #
58
+ # @since 0.0.1
59
+ # @return [OctocatHerder::Repository]
28
60
  def repo
29
- @repo = OctocatHerder::Repository.new(@raw['repo'], connection)
61
+ @repo ||= OctocatHerder::Repository.new(@raw['repo'], connection)
30
62
  end
31
63
 
64
+ private
65
+
32
66
  def addtional_attributes
33
67
  ['user_login', 'user_id', 'user_avatar_url', 'user_url']
34
68
  end