github_api 0.9.7 → 0.10.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.
Files changed (60) hide show
  1. data/README.md +24 -14
  2. data/features/cassettes/media/get_default.yml +84 -0
  3. data/features/cassettes/media/get_full_json.yml +83 -0
  4. data/features/cassettes/media/get_html_json.yml +81 -0
  5. data/features/cassettes/media/get_raw_json.yml +80 -0
  6. data/features/cassettes/media/get_text_json.yml +80 -0
  7. data/features/cassettes/repos/stats/commits.yml +80 -0
  8. data/features/cassettes/repos/stats/contribs.yml +221 -0
  9. data/features/cassettes/repos/stats/frequency.yml +54 -0
  10. data/features/cassettes/repos/stats/participation.yml +68 -0
  11. data/features/cassettes/repos/stats/punch.yml +76 -0
  12. data/features/cassettes/search/users_keyword.yml +70 -0
  13. data/features/gitignore.feature +1 -1
  14. data/features/media_type.feature +76 -0
  15. data/features/repos/statistics.feature +60 -0
  16. data/features/search.feature +11 -0
  17. data/features/step_definitions/github_api_steps.rb +4 -0
  18. data/lib/github_api.rb +2 -0
  19. data/lib/github_api/arguments.rb +1 -1
  20. data/lib/github_api/authorization.rb +1 -1
  21. data/lib/github_api/connection.rb +5 -7
  22. data/lib/github_api/core_ext/deep_merge.rb +13 -0
  23. data/lib/github_api/git_data/trees.rb +1 -1
  24. data/lib/github_api/gitignore.rb +5 -4
  25. data/lib/github_api/markdown.rb +7 -5
  26. data/lib/github_api/mime_type.rb +19 -41
  27. data/lib/github_api/paged_request.rb +1 -1
  28. data/lib/github_api/parameter_filter.rb +1 -1
  29. data/lib/github_api/params_hash.rb +59 -22
  30. data/lib/github_api/repos.rb +9 -3
  31. data/lib/github_api/repos/pub_sub_hubbub.rb +4 -2
  32. data/lib/github_api/repos/statistics.rb +94 -0
  33. data/lib/github_api/request.rb +17 -28
  34. data/lib/github_api/response_wrapper.rb +8 -0
  35. data/lib/github_api/say.rb +2 -1
  36. data/lib/github_api/scopes.rb +3 -2
  37. data/lib/github_api/search.rb +3 -3
  38. data/lib/github_api/utils/url.rb +5 -2
  39. data/lib/github_api/version.rb +2 -2
  40. data/spec/fixtures/repos/commit_activity.json +15 -0
  41. data/spec/fixtures/repos/contribs.json +20 -0
  42. data/spec/fixtures/repos/frequency.json +7 -0
  43. data/spec/fixtures/repos/participation.json +110 -0
  44. data/spec/fixtures/repos/punch_card.json +17 -0
  45. data/spec/github/arguments/parse_spec.rb +2 -2
  46. data/spec/github/core_ext/deep_merge_spec.rb +23 -0
  47. data/spec/github/git_data/trees/create_spec.rb +6 -0
  48. data/spec/github/gitignore/get_spec.rb +1 -1
  49. data/spec/github/mime_type_spec.rb +24 -55
  50. data/spec/github/params_hash_spec.rb +64 -0
  51. data/spec/github/pull_requests/list_spec.rb +1 -1
  52. data/spec/github/repos/statistics/code_frequency_spec.rb +25 -0
  53. data/spec/github/repos/statistics/commit_activity_spec.rb +29 -0
  54. data/spec/github/repos/statistics/contributors_spec.rb +29 -0
  55. data/spec/github/repos/statistics/participation_spec.rb +25 -0
  56. data/spec/github/repos/statistics/punch_card_spec.rb +25 -0
  57. data/spec/github/request_spec.rb +10 -11
  58. data/spec/github/response_wrapper/overwrites_spec.rb +19 -0
  59. data/spec/github/users/keys/update_spec.rb +1 -1
  60. metadata +73 -34
@@ -33,7 +33,7 @@ module Github
33
33
  params[PARAM_PAGE] = default_page
34
34
  end
35
35
 
36
- current_api.get_request(path, params)
36
+ current_api.get_request(path, ParamsHash.new(params))
37
37
  end
38
38
 
39
39
  end # PagedRequest
@@ -10,7 +10,7 @@ module Github
10
10
  #
11
11
  def filter!(keys, params, options={:recursive => true}) # :nodoc:
12
12
  case params
13
- when Hash
13
+ when Hash, ParamsHash
14
14
  params.keys.each do |k, v|
15
15
  unless (keys.include?(k) or Github::Validations::VALID_API_KEYS.include?(k))
16
16
  params.delete(k)
@@ -1,30 +1,67 @@
1
+ # encoding: utf-8
2
+
1
3
  module Github
2
- class ParamsHash < ::Hash
3
-
4
- def initialize(*args, &block)
5
- hash = args.extract_options!
6
- # debugger
7
- # debugger
8
- # normalize_keys!(hash)
9
- # debugger
10
- super[hash]
4
+
5
+ # Class responsible for holding request parameters
6
+ class ParamsHash < DelegateClass(Hash)
7
+ include Normalizer
8
+ include MimeType
9
+
10
+ def initialize(hash)
11
+ super(normalize!(Hash[hash]))
11
12
  end
12
13
 
13
- def normalize_keys!(params)
14
- case params
15
- when Hash
16
- params.keys.each do |k|
17
- params[k.to_s] = params.delete(k)
18
- normalize_keys!(params[k.to_s])
19
- end
20
- when Array
21
- params.map! do |el|
22
- normalize_keys!(el)
23
- end
14
+ # Extract and parse media type param
15
+ #
16
+ # [.version].param[+json]
17
+ #
18
+ def media
19
+ parse(self.delete('media'))
20
+ end
21
+
22
+ # Return accept header if present
23
+ #
24
+ def accept
25
+ if has_key?('accept')
26
+ self.delete('accept')
27
+ elsif has_key?('media')
28
+ media
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ # Extract request data from paramters
35
+ #
36
+ def data
37
+ if has_key?('data') && !self['data'].nil?
38
+ return self.delete('data')
24
39
  else
25
- params.to_s
40
+ return self.to_hash
41
+ end
42
+ end
43
+
44
+ # Any client configuration options
45
+ #
46
+ def options
47
+ hash = has_key?('options') ? self.delete('options') : {}
48
+ if value = accept
49
+ hash[:headers] = {} unless hash.has_key?(:headers)
50
+ hash[:headers]['Accept'] = value
51
+ end
52
+ hash[:raw] = has_key?('raw') ? self.delete('raw') : false
53
+ hash
54
+ end
55
+
56
+ # Update hash with default parameters for non existing keys
57
+ #
58
+ def merge_default(defaults)
59
+ if defaults && !defaults.empty?
60
+ defaults.each do |key, value|
61
+ self[key] = value unless self.has_key?(key)
62
+ end
26
63
  end
27
- return params
64
+ self
28
65
  end
29
66
 
30
67
  end # ParamsHash
@@ -16,6 +16,7 @@ module Github
16
16
  :Keys => 'keys',
17
17
  :Merging => 'merging',
18
18
  :PubSubHubbub => 'pub_sub_hubbub',
19
+ :Statistics => 'statistics',
19
20
  :Statuses => 'statuses'
20
21
 
21
22
  DEFAULT_REPO_OPTIONS = {
@@ -94,6 +95,11 @@ module Github
94
95
  @pubsubhubbub ||= ApiFactory.new('Repos::PubSubHubbub', current_options.merge(options), &block)
95
96
  end
96
97
 
98
+ # Access to Repos::Statistics API
99
+ def stats(options={}, &block)
100
+ @stats ||= ApiFactory.new('Repos::Statistics', current_options.merge(options), &block)
101
+ end
102
+
97
103
  # Access to Repos::Statuses API
98
104
  def statuses(options={}, &block)
99
105
  @statuses ||= ApiFactory.new('Repos::Statuses', current_options.merge(options), &block)
@@ -209,9 +215,9 @@ module Github
209
215
 
210
216
  # Requires authenticated user
211
217
  if (org = params.delete("org"))
212
- post_request("/orgs/#{org}/repos", DEFAULT_REPO_OPTIONS.merge(params))
218
+ post_request("/orgs/#{org}/repos", params.merge_default(DEFAULT_REPO_OPTIONS))
213
219
  else
214
- post_request("/user/repos", DEFAULT_REPO_OPTIONS.merge(params))
220
+ post_request("/user/repos", params.merge_default(DEFAULT_REPO_OPTIONS))
215
221
  end
216
222
  end
217
223
 
@@ -284,7 +290,7 @@ module Github
284
290
  end
285
291
  params = arguments.params
286
292
 
287
- patch_request("/repos/#{user}/#{repo}", DEFAULT_REPO_OPTIONS.merge(params))
293
+ patch_request("/repos/#{user}/#{repo}", params.merge_default(DEFAULT_REPO_OPTIONS))
288
294
  end
289
295
 
290
296
  # Delete a repository
@@ -25,8 +25,9 @@ module Github
25
25
  def subscribe(*args)
26
26
  params = arguments(args, :required => [:topic, :callback]).params
27
27
  _merge_action!("subscribe", topic, callback, params)
28
+ params['options'] = OPTIONS
28
29
 
29
- post_request("/hub", params, OPTIONS)
30
+ post_request("/hub", params)
30
31
  end
31
32
 
32
33
  # Unsubscribe from existing topic/event through pubsubhubbub
@@ -46,8 +47,9 @@ module Github
46
47
  def unsubscribe(*args)
47
48
  params = arguments(args, :required => [:topic, :callback]).params
48
49
  _merge_action!("unsubscribe", topic, callback, params)
50
+ params['options'] = OPTIONS
49
51
 
50
- post_request("/hub", params, OPTIONS)
52
+ post_request("/hub", params)
51
53
  end
52
54
 
53
55
  # Subscribe repository to service hook through pubsubhubbub
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+
3
+ module Github
4
+
5
+ # The Repository Statistics API allows you to fetch the data that GitHub uses
6
+ # for visualizing different types of repository activity.
7
+ class Repos::Statistics < API
8
+
9
+ # Get contributors list with additions, deletions, and commit counts
10
+ #
11
+ # = Examples
12
+ #
13
+ # github = Github.new
14
+ # github.repos.stats.contributors user: '...', repo: '...'
15
+ # github.repos.stats.contributors user: '...', repo: '...' { |stat| ... }
16
+ #
17
+ def contributors(*args)
18
+ arguments(args, :required => [:user, :repo])
19
+ params = arguments.params
20
+
21
+ response = get_request("/repos/#{user}/#{repo}/stats/contributors", params)
22
+ return response unless block_given?
23
+ response.each { |el| yield el }
24
+ end
25
+
26
+ # Get the last year of commit activity data
27
+ #
28
+ # Returns the last year of commit activity grouped by week.
29
+ # The days array is a group of commits per day, starting on Sunday
30
+ #
31
+ # = Examples
32
+ #
33
+ # github = Github.new
34
+ # github.repos.stats.commit_activity user: '...', repo: '...'
35
+ # github.repos.stats.commit_activity user: '...', repo: '...' { |stat| ... }
36
+ #
37
+ def commit_activity(*args)
38
+ arguments(args, :required => [:user, :repo])
39
+ params = arguments.params
40
+
41
+ response = get_request("/repos/#{user}/#{repo}/stats/commit_activity", params)
42
+ return response unless block_given?
43
+ response.each { |el| yield el }
44
+ end
45
+
46
+ # Get the number of additions and deletions per week
47
+ #
48
+ # = Examples
49
+ #
50
+ # github = Github.new
51
+ # github.repos.stats.code_frequency user: '...', repo: '...'
52
+ # github.repos.stats.code_frequency user: '...', repo: '...' { |stat| ... }
53
+ #
54
+ def code_frequency(*args)
55
+ arguments(args, :required => [:user, :repo])
56
+ params = arguments.params
57
+
58
+ get_request("/repos/#{user}/#{repo}/stats/code_frequency", params)
59
+ end
60
+
61
+ # Get the weekly commit count for the repo owner and everyone else
62
+ #
63
+ # = Examples
64
+ #
65
+ # github = Github.new
66
+ # github.repos.stats.participation user: '...', repo: '...'
67
+ # github.repos.stats.participation user: '...', repo: '...' { |stat| ... }
68
+ #
69
+ def participation(*args)
70
+ arguments(args, :required => [:user, :repo])
71
+ params = arguments.params
72
+
73
+ get_request("/repos/#{user}/#{repo}/stats/participation", params)
74
+ end
75
+
76
+ # Get the number of commits per hour in each day
77
+ #
78
+ # = Examples
79
+ #
80
+ # github = Github.new
81
+ # github.repos.stats.punch_card user: '...', repo: '...'
82
+ # github.repos.stats.punch_card user: '...', repo: '...' { |stat| ... }
83
+ #
84
+ def punch_card(*args)
85
+ arguments(args, :required => [:user, :repo])
86
+ params = arguments.params
87
+
88
+ response = get_request("/repos/#{user}/#{repo}/stats/punch_card", params)
89
+ return response unless block_given?
90
+ response.each { |el| yield el }
91
+ end
92
+
93
+ end # Repos::Statistics
94
+ end # Github
@@ -8,34 +8,35 @@ module Github
8
8
  METHODS = [:get, :post, :put, :delete, :patch]
9
9
  METHODS_WITH_BODIES = [ :post, :put, :patch ]
10
10
 
11
- def get_request(path, params={}, options={})
12
- request(:get, path, params, options).auto_paginate
11
+ def get_request(path, params={})
12
+ request(:get, path, params).auto_paginate
13
13
  end
14
14
 
15
- def patch_request(path, params={}, options={})
16
- request(:patch, path, params, options)
15
+ def patch_request(path, params={})
16
+ request(:patch, path, params)
17
17
  end
18
18
 
19
- def post_request(path, params={}, options={})
20
- request(:post, path, params, options)
19
+ def post_request(path, params={})
20
+ request(:post, path, params)
21
21
  end
22
22
 
23
- def put_request(path, params={}, options={})
24
- request(:put, path, params, options)
23
+ def put_request(path, params={})
24
+ request(:put, path, params)
25
25
  end
26
26
 
27
- def delete_request(path, params={}, options={})
28
- request(:delete, path, params, options)
27
+ def delete_request(path, params={})
28
+ request(:delete, path, params)
29
29
  end
30
30
 
31
- def request(method, path, params, options) # :nodoc:
31
+ def request(method, path, params) # :nodoc:
32
32
  if !METHODS.include?(method)
33
33
  raise ArgumentError, "unkown http method: #{method}"
34
34
  end
35
35
 
36
- puts "EXECUTED: #{method} - #{path} with #{params} and #{options}" if ENV['DEBUG']
36
+ puts "EXECUTED: #{method} - #{path} with PARAMS: #{params}" if ENV['DEBUG']
37
37
 
38
- conn = connection(options.merge(current_options))
38
+ conn_options = params.options.merge(current_options)
39
+ conn = connection(conn_options)
39
40
  if conn.path_prefix != '/' && path.index(conn.path_prefix) != 0
40
41
  path = (conn.path_prefix + path).gsub(/\/(\/)*/, '/')
41
42
  end
@@ -43,27 +44,15 @@ module Github
43
44
  response = conn.send(method) do |request|
44
45
  case method.to_sym
45
46
  when *(METHODS - METHODS_WITH_BODIES)
46
- request.body = params.delete('data') if params.has_key?('data')
47
- request.url(path, params)
47
+ request.body = params.data if params.has_key?('data')
48
+ request.url(path, params.to_hash)
48
49
  when *METHODS_WITH_BODIES
49
50
  request.path = path
50
- request.body = extract_data_from_params(params) unless params.empty?
51
+ request.body = params.data unless params.empty?
51
52
  end
52
53
  end
53
54
  ResponseWrapper.new(response, self)
54
55
  end
55
56
 
56
- private
57
-
58
- def extract_data_from_params(params) # :nodoc:
59
- return params['data'] if params.has_key?('data') and !params['data'].nil?
60
- return params
61
- end
62
-
63
- def _extract_mime_type(params, options) # :nodoc:
64
- options['resource'] = params['resource'] ? params.delete('resource') : ''
65
- options['mime_type'] = params['resource'] ? params.delete('mime_type') : ''
66
- end
67
-
68
57
  end # Request
69
58
  end # Github
@@ -22,6 +22,14 @@ module Github
22
22
  @env = response.env
23
23
  end
24
24
 
25
+ # Overwrite methods to hash keys
26
+ #
27
+ ['id', 'type', 'fork'].each do |method_name|
28
+ define_method(method_name) do
29
+ self.body.fetch(method_name.to_s)
30
+ end
31
+ end
32
+
25
33
  # Request url
26
34
  #
27
35
  def url
@@ -14,8 +14,9 @@ module Github
14
14
  def say(*args)
15
15
  params = arguments(*args).params
16
16
  params[:s] = args.shift unless args.empty?
17
+ params['raw'] = true
17
18
 
18
- get_request('/octocat', params, :raw => true)
19
+ get_request('/octocat', params)
19
20
  end
20
21
 
21
22
  end # Say
@@ -9,8 +9,9 @@ module Github
9
9
  # github = Github.new :oauth_token => 'token'
10
10
  # github.scopes.all
11
11
  #
12
- def list(params={})
13
- response = get_request("/user", params)
12
+ def list(*args)
13
+ arguments(args)
14
+ response = get_request("/user", arguments.params)
14
15
  response.headers.oauth_scopes ? response.headers.oauth_scopes.split(',') : response
15
16
  end
16
17
  alias :all :list
@@ -21,7 +21,7 @@ module Github
21
21
  required = ['owner', 'repo', 'state', 'keyword']
22
22
  arguments(args, :required => required)
23
23
 
24
- get_request("/legacy/issues/search/#{owner}/#{repo}/#{state}/#{escape(keyword)}", arguments.params)
24
+ get_request("/legacy/issues/search/#{owner}/#{repo}/#{state}/#{escape_uri(keyword)}", arguments.params)
25
25
  end
26
26
 
27
27
  # Search repositories
@@ -39,7 +39,7 @@ module Github
39
39
  def repos(*args)
40
40
  arguments(args, :required => [:keyword])
41
41
 
42
- get_request("/legacy/repos/search/#{escape(keyword)}", arguments.params)
42
+ get_request("/legacy/repos/search/#{escape_uri(keyword)}", arguments.params)
43
43
  end
44
44
  alias :repositories :repos
45
45
 
@@ -57,7 +57,7 @@ module Github
57
57
  def users(*args)
58
58
  arguments(args, :required => [:keyword])
59
59
 
60
- get_request("/legacy/user/search/#{escape(keyword)}", arguments.params)
60
+ get_request("/legacy/user/search/#{escape_uri(keyword)}", arguments.params)
61
61
  end
62
62
 
63
63
  # Search email
@@ -1,4 +1,5 @@
1
1
  require 'cgi'
2
+ require 'addressable/uri'
2
3
 
3
4
  module Github
4
5
  module Utils
@@ -9,9 +10,11 @@ module Github
9
10
 
10
11
  KEY_VALUE_SEP = '='.freeze
11
12
 
12
- def escape(s) CGI.escape s.to_s end
13
+ def escape_uri(s) Addressable::URI.escape(s.to_s) end
13
14
 
14
- def unescape(s) CGI.unescape s.to_s end
15
+ def escape(s) CGI.escape(s.to_s) end
16
+
17
+ def unescape(s) CGI.unescape(s.to_s) end
15
18
 
16
19
  def build_query(params)
17
20
  params.map { |k, v|
@@ -3,8 +3,8 @@
3
3
  module Github
4
4
  module VERSION
5
5
  MAJOR = 0
6
- MINOR = 9
7
- PATCH = 7
6
+ MINOR = 10
7
+ PATCH = 0
8
8
  BUILD = nil
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.');
@@ -0,0 +1,15 @@
1
+ [
2
+ {
3
+ "days": [
4
+ 0,
5
+ 3,
6
+ 26,
7
+ 20,
8
+ 39,
9
+ 1,
10
+ 0
11
+ ],
12
+ "total": 89,
13
+ "week": "2012-05-06"
14
+ }
15
+ ]