octocat_herder 0.0.2 → 0.1.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/.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