git_hub_bub 1.0.1 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b99f20d88fb45334555be481192824a663b56834c77bdd8f66bb79172b129455
4
- data.tar.gz: aa099a50aa5e657d2377c8d151d811e99720d41e4678097b9973b0d4a847d8c5
3
+ metadata.gz: 4a1fb065fefd4671e1af0e588b6cfb6ea5d19261d61af52196c3a6e78eb2640a
4
+ data.tar.gz: 6904fbfcd325633d8de9d6a332d8fe0700e58685b66d612598a007879526affc
5
5
  SHA512:
6
- metadata.gz: ef2dbcabddf20e7cf6a9edef66d388203611afe876ff4fdc304e35de2af5a9e19189d773da365d4e9405a375353408accfd57038599c6df96421af48310f59fe
7
- data.tar.gz: '0428e888f112051cab2d5d952305f39a747d662e43b2b4fe4ed43a7d5635f46aaad4b72a5b84f9f39c24969bacbc38c75f919f5936b10608b68f46f7ca7d0498'
6
+ metadata.gz: c8a89709d49289a988464527e23b88e8eaad3115d26714d50df2679078e06f0d735c08a5b06487f56b6619b15d90022c37031b345d91f83f8c69efe7d58d7ec8
7
+ data.tar.gz: 53fd2d767153a4ba3e38d7e8e8e5cbc747ddee4797247e3584771f5605c036a24a73e2e5c8721b88b548502729cf2aec9248b5d7ea2c2d81ee366e3acde2f9d3
@@ -0,0 +1,11 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: /
5
+ schedule:
6
+ interval: monthly
7
+
8
+ - package-ecosystem: github-actions
9
+ directory: /
10
+ schedule:
11
+ interval: monthly
@@ -0,0 +1,43 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ # Avoid duplicate builds on PRs.
6
+ branches:
7
+ - main
8
+ pull_request:
9
+
10
+ jobs:
11
+ lint:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+
16
+ - name: Set up Ruby
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: '3.2'
20
+ bundler-cache: true
21
+
22
+ - name: Run StandardRB
23
+ run: bundle exec standardrb
24
+
25
+ test:
26
+ runs-on: ubuntu-latest
27
+
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ ruby-version: ['3.2', '3.3', '3.4', '4.0']
32
+
33
+ steps:
34
+ - uses: actions/checkout@v6
35
+
36
+ - name: Set up Ruby ${{ matrix.ruby-version }}
37
+ uses: ruby/setup-ruby@v1
38
+ with:
39
+ ruby-version: ${{ matrix.ruby-version }}
40
+ bundler-cache: true
41
+
42
+ - name: Run tests
43
+ run: bundle exec rake test
data/.standard.yml ADDED
@@ -0,0 +1 @@
1
+ ruby_version: 3.2
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
- gemspec
3
+ gemspec
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # GitHubBub
2
2
 
3
- [![Build Status](https://travis-ci.org/schneems/git_hub_bub.svg?branch=master)](https://travis-ci.org/schneems/git_hub_bub)
3
+ [![CI](https://github.com/schneems/git_hub_bub/actions/workflows/ci.yml/badge.svg)](https://github.com/schneems/git_hub_bub/actions/workflows/ci.yml)
4
4
  [![Help Contribute to Open Source](https://www.codetriage.com/schneems/git_hub_bub/badges/users.svg)](https://www.codetriage.com/schneems/git_hub_bub)
5
5
 
6
6
  A low level GitHub client that makes the disgusting issue of header based url pagination simple.
data/Rakefile CHANGED
@@ -1,15 +1,14 @@
1
- require 'bundler/gem_tasks'
2
- require 'git_hub_bub'
1
+ require "bundler/gem_tasks"
2
+ require "git_hub_bub"
3
3
 
4
- task :default => [:test]
4
+ task default: [:test]
5
5
 
6
- require 'rake'
7
- require 'rake/testtask'
6
+ require "rake"
7
+ require "rake/testtask"
8
8
 
9
9
  Rake::TestTask.new(:test) do |t|
10
- t.libs << 'lib'
11
- t.libs << 'test'
12
- t.pattern = 'test/**/*_test.rb'
10
+ t.libs << "lib"
11
+ t.libs << "test"
12
+ t.pattern = "test/**/*_test.rb"
13
13
  t.verbose = true
14
14
  end
15
-
data/changelog.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # Changelog
2
2
 
3
- ## Master
3
+ ## Unreleased
4
+
5
+ ## 2.0.0
6
+
7
+ - Changed: Minimum supported Ruby version updated to 3.2.
8
+ - Fix: Responses that don't have a `last_url` in the link no longer error.
4
9
 
5
10
  ## 1.0.1
6
11
 
data/git_hub_bub.gemspec CHANGED
@@ -1,32 +1,32 @@
1
- # -*- encoding: utf-8 -*-
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'git_hub_bub/version'
3
+ require "git_hub_bub/version"
5
4
 
6
5
  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"
6
+ gem.name = "git_hub_bub"
7
+ gem.version = GitHubBub::VERSION
8
+ gem.authors = ["Richard Schneeman"]
9
+ gem.email = ["richard.schneeman+rubygems@gmail.com"]
10
+ gem.description = "git_hub_bub makes github requests"
11
+ gem.summary = "git_hub_bub makes github requests"
12
+ gem.homepage = "https://github.com/schneems/git_hub_bub"
13
+ gem.license = "MIT"
15
14
 
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)/})
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
19
17
  gem.require_paths = ["lib"]
20
18
 
21
19
  gem.add_dependency "rrrretry"
22
20
  gem.add_dependency "excon"
21
+ gem.add_dependency "base64"
23
22
  gem.add_development_dependency "timecop"
24
23
  gem.add_development_dependency "test-unit"
25
24
  gem.add_development_dependency "mocha"
26
25
  gem.add_development_dependency "rake"
27
- gem.add_development_dependency "vcr", '~> 2.5.0'
28
- gem.add_development_dependency "webmock", '~> 1.11.0'
26
+ gem.add_development_dependency "vcr"
27
+ gem.add_development_dependency "webmock"
29
28
  gem.add_development_dependency "dotenv"
29
+ gem.add_development_dependency "standard"
30
30
 
31
- gem.required_ruby_version = '>= 2.2'
31
+ gem.required_ruby_version = ">= 3.2"
32
32
  end
@@ -4,22 +4,23 @@ module GitHubBub
4
4
  class RequestError < StandardError; end
5
5
 
6
6
  class Request
7
- attr_accessor :url, :options, :token
8
- BASE_URI = 'https://api.github.com'
9
- USER_AGENT ||= SecureRandom.hex(16)
7
+ attr_accessor :url, :options
8
+ attr_writer :token
9
+ BASE_URI = "https://api.github.com"
10
+ USER_AGENT = defined?(USER_AGENT) ? USER_AGENT : SecureRandom.hex(16)
10
11
  GITHUB_VERSION = "vnd.github.3.raw+json"
11
- EXTRA_HEADERS ||= {}
12
- BASE_HEADERS = EXTRA_HEADERS.merge({'Accept' => "application/#{GITHUB_VERSION}", "User-Agent" => USER_AGENT})
13
- BASE_OPTIONS = { omit_default_port: true }
14
- RETRIES = 1
15
- RAISE_ON_FAIL = ENV["GIT_HUB_BUB_RAISE_ON_FAIL"]
12
+ EXTRA_HEADERS = defined?(EXTRA_HEADERS) ? EXTRA_HEADERS : {}
13
+ BASE_HEADERS = EXTRA_HEADERS.merge({"Accept" => "application/#{GITHUB_VERSION}", "User-Agent" => USER_AGENT})
14
+ BASE_OPTIONS = {omit_default_port: true}
15
+ RETRIES = 1
16
+ RAISE_ON_FAIL = ENV["GIT_HUB_BUB_RAISE_ON_FAIL"]
16
17
 
17
18
  def initialize(url, query = {}, options = {})
18
- self.url = url =~ /^http(\w?)\:\/\// ? url : File.join(BASE_URI, url)
19
- @skip_token = options.delete(:skip_token)
20
- self.options = BASE_OPTIONS.merge(options || {})
21
- self.options[:query] = query if query && !query.empty?
22
- self.options[:headers] = BASE_HEADERS.merge(options[:headers]|| {})
19
+ self.url = /^http(\w?):\/\//.match?(url) ? url : File.join(BASE_URI, url)
20
+ @skip_token = options.delete(:skip_token)
21
+ self.options = BASE_OPTIONS.merge(options || {})
22
+ self.options[:query] = query if query && !query.empty?
23
+ self.options[:headers] = BASE_HEADERS.merge(options[:headers] || {})
23
24
  end
24
25
 
25
26
  def skip_token?
@@ -27,7 +28,7 @@ module GitHubBub
27
28
  end
28
29
 
29
30
  def self.head(url, query = {}, options = {})
30
- self.new(url, query, options).head
31
+ new(url, query, options).head
31
32
  end
32
33
 
33
34
  def head
@@ -37,19 +38,21 @@ module GitHubBub
37
38
  end
38
39
 
39
40
  def self.get(url, query = {}, options = {})
40
- self.new(url, query, options).get
41
+ new(url, query, options).get
41
42
  end
42
43
 
43
44
  def get
44
45
  wrap_request do
45
46
  ex = Excon.get(url, options)
46
- ex = Excon.get(@location, options) if @location = ex.headers["Location"]
47
+ if (@location = ex.headers["Location"])
48
+ ex = Excon.get(@location, options)
49
+ end
47
50
  ex
48
51
  end
49
52
  end
50
53
 
51
54
  def self.post(url, query = {}, options = {})
52
- self.new(url, query, options).post
55
+ new(url, query, options).post
53
56
  end
54
57
 
55
58
  def post
@@ -59,7 +62,7 @@ module GitHubBub
59
62
  end
60
63
 
61
64
  def self.patch(url, query = {}, options = {})
62
- self.new(url, query, options).patch
65
+ new(url, query, options).patch
63
66
  end
64
67
 
65
68
  def patch
@@ -69,7 +72,7 @@ module GitHubBub
69
72
  end
70
73
 
71
74
  def self.put(url, query = {}, options = {})
72
- self.new(url, query, options).put
75
+ new(url, query, options).put
73
76
  end
74
77
 
75
78
  def put
@@ -79,7 +82,7 @@ module GitHubBub
79
82
  end
80
83
 
81
84
  def self.delete(url, query = {}, options = {})
82
- self.new(url, query, options).delete
85
+ new(url, query, options).delete
83
86
  end
84
87
 
85
88
  def delete
@@ -97,9 +100,9 @@ module GitHubBub
97
100
  end
98
101
 
99
102
  if RAISE_ON_FAIL
100
- raise RequestError, "message: '#{response.json_body['message']}', url: '#{url}', response: '#{response.inspect}'" unless response.success?
103
+ raise RequestError, "message: '#{response.json_body["message"]}', url: '#{url}', response: '#{response.inspect}'" unless response.success?
101
104
  end
102
- return response
105
+ response
103
106
  end
104
107
 
105
108
  # do they take query params? do they take :body?
@@ -114,22 +117,22 @@ module GitHubBub
114
117
  end
115
118
 
116
119
  def token
117
- @token ||= if options[:headers] && token_string = options[:headers]["Authorization"]
120
+ @token ||= if options[:headers] && (token_string = options[:headers]["Authorization"])
118
121
  token_string.split(/\s/).last
119
- elsif options[:query] && token = options[:query].delete(:token)
120
- token
122
+ elsif options[:query] && (query_token = options[:query].delete(:token))
123
+ query_token
121
124
  else
122
125
  skip_token?
123
126
  end
124
127
  end
125
- alias :token? :token
128
+ alias_method :token?, :token
126
129
 
127
130
  def self.set_before_callback(&block)
128
131
  before_callbacks << block
129
132
  end
130
133
 
131
134
  def self.before_callbacks
132
- @before_callbacks ||=[]
135
+ @before_callbacks ||= []
133
136
  end
134
137
 
135
138
  def self.clear_callbacks
@@ -138,7 +141,7 @@ module GitHubBub
138
141
 
139
142
  def before_callbacks!
140
143
  self.class.before_callbacks.each do |callback|
141
- run_callback &callback
144
+ run_callback(&callback)
142
145
  end
143
146
  end
144
147
 
@@ -1,104 +1,106 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'date'
3
+ require "date"
4
4
 
5
5
  module GitHubBub
6
6
  class Response < Excon::Response
7
- def self.create(response)
8
- self.new(response.data)
9
- end
10
-
11
- def rate_limit_remaining
12
- limit_remaining = headers["X-RateLimit-Limit"]
13
- Integer(limit_remaining)
14
- end
15
-
16
- def rate_limit_reset_time_left # in seconds
17
- utc_epoch_seconds = headers["X-RateLimit-Reset"]
18
- utc_epoch_seconds = Integer(utc_epoch_seconds)
19
- return utc_epoch_seconds - Time.now.utc.to_i
20
- end
21
-
22
- # When no time is left we want to sleep until our limit is reset
23
- # i.e. remaining is 1 so time/1 => time
24
- #
25
- # When we have plenty of requests left then we want to sleep for too long
26
- # i.e. time / 1000 => smaller amount of time
27
- def rate_limit_sleep!(bypass_sleep: false)
7
+ def self.create(response)
8
+ new(response.data)
9
+ end
10
+
11
+ def rate_limit_remaining
12
+ limit_remaining = headers["X-RateLimit-Limit"]
13
+ Integer(limit_remaining)
14
+ end
15
+
16
+ def rate_limit_reset_time_left # in seconds
17
+ utc_epoch_seconds = headers["X-RateLimit-Reset"]
18
+ utc_epoch_seconds = Integer(utc_epoch_seconds)
19
+ utc_epoch_seconds - Time.now.utc.to_i
20
+ end
21
+
22
+ # When no time is left we want to sleep until our limit is reset
23
+ # i.e. remaining is 1 so time/1 => time
24
+ #
25
+ # When we have plenty of requests left then we want to sleep for too long
26
+ # i.e. time / 1000 => smaller amount of time
27
+ def rate_limit_sleep!(bypass_sleep: false)
28
28
  remaining = rate_limit_remaining
29
29
  time_left = rate_limit_reset_time_left
30
30
  return 0 if time_left <= 0
31
31
  return 0 if remaining > 1000
32
32
 
33
- if remaining > 0
34
- val = time_left / remaining.to_f
33
+ val = if remaining > 0
34
+ time_left / remaining.to_f
35
35
  else
36
- val = time_left
36
+ time_left
37
37
  end
38
38
  sleep(val) unless bypass_sleep
39
- return val
40
- end
39
+ val
40
+ end
41
41
 
42
- def json_body
43
- ::JSON.parse(self.body)
44
- end
42
+ def json_body
43
+ ::JSON.parse(body)
44
+ end
45
45
 
46
- def success?
46
+ def success?
47
47
  status.to_s =~ /^2.*/
48
- end
48
+ end
49
49
 
50
- def pagination
51
- @pagination ||= parse_pagination
52
- end
50
+ def pagination
51
+ @pagination ||= parse_pagination
52
+ end
53
53
 
54
- def parsed_response
54
+ def parsed_response
55
55
  response.body.inspect
56
- end
57
-
58
- def next_url
59
- pagination['next_url']
60
- end
61
-
62
- def prev_url
63
- pagination['prev_url']
64
- end
65
- alias :previous_url :prev_url
66
-
67
- def last_url
68
- pagination['last_url']
69
- end
70
-
71
- def first_url
72
- pagination['first_url']
73
- end
74
-
75
- def last_page?
76
- return true if next_url.nil?
77
- last_page_number = page_number_from_url(last_url)
78
- next_page_number = page_number_from_url(next_url)
79
- return next_page_number > last_page_number
80
- end
81
-
82
- def first_page?
83
- return true if first_url.nil?
84
- return false
85
- end
86
-
87
- def page_number_from_url(url)
88
- query = ::URI.parse(url).query
89
- ::CGI.parse(query)["page"].first.to_i
90
- end
56
+ end
57
+
58
+ def next_url
59
+ pagination["next_url"]
60
+ end
61
+
62
+ def prev_url
63
+ pagination["prev_url"]
64
+ end
65
+ alias_method :previous_url, :prev_url
66
+
67
+ def last_url
68
+ pagination["last_url"]
69
+ end
70
+
71
+ def first_url
72
+ pagination["first_url"]
73
+ end
74
+
75
+ def last_page?
76
+ return true if next_url.nil?
77
+ return false if last_url.nil? # A next page exists, but no "last" page, keep going.
78
+ last_page_number = page_number_from_url(last_url)
79
+ next_page_number = page_number_from_url(next_url)
80
+ next_page_number > last_page_number
81
+ end
82
+
83
+ def first_page?
84
+ return true if first_url.nil?
85
+ false
86
+ end
87
+
88
+ def page_number_from_url(url)
89
+ query = ::URI.parse(url).query
90
+ params = ::URI.decode_www_form(query).to_h
91
+ params["page"].to_i
92
+ end
91
93
 
92
94
  def header_links
93
- (headers['link'] || headers['Link'] || "").split(',')
94
- end
95
-
96
- def parse_pagination
97
- header_links.each_with_object({}) do |element, hash|
98
- key = element[/rel=["'](.*)['"]/, 1]
99
- value = element[/<(.*)>/, 1]
100
- hash["#{key}_url"] = value
101
- end
102
- end
103
- end
104
- end
95
+ (headers["link"] || headers["Link"] || "").split(",")
96
+ end
97
+
98
+ def parse_pagination
99
+ header_links.each_with_object({}) do |element, hash|
100
+ key = element[/rel=["'](.*)['"]/, 1]
101
+ value = element[/<(.*)>/, 1]
102
+ hash["#{key}_url"] = value
103
+ end
104
+ end
105
+ end
106
+ end
@@ -1,3 +1,3 @@
1
1
  module GitHubBub
2
- VERSION = "1.0.1"
2
+ VERSION = "2.0.0"
3
3
  end
data/lib/git_hub_bub.rb CHANGED
@@ -1,26 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'securerandom'
4
- require 'json'
5
- require 'uri'
6
- require 'cgi'
3
+ require "securerandom"
4
+ require "json"
5
+ require "uri"
7
6
 
8
- require 'excon'
9
- require 'rrrretry'
7
+ require "excon"
8
+ require "rrrretry"
10
9
 
11
- require 'git_hub_bub/request'
12
- require 'git_hub_bub/response'
10
+ require "git_hub_bub/request"
11
+ require "git_hub_bub/response"
13
12
 
14
13
  module GitHubBub
15
14
  class << self
16
-
17
15
  def valid_token?(token)
18
- response = Request.get("https://#{ENV['GITHUB_APP_ID']}:#{ENV['GITHUB_APP_SECRET']}@api.github.com/applications/#{ENV['GITHUB_APP_ID']}/tokens/#{token}", {}, {skip_token: true})
16
+ response = Request.get("https://#{ENV["GITHUB_APP_ID"]}:#{ENV["GITHUB_APP_SECRET"]}@api.github.com/applications/#{ENV["GITHUB_APP_ID"]}/tokens/#{token}", {}, {skip_token: true})
19
17
  return response if response.success?
20
- return false if response.status == 404
18
+ false if response.status == 404
21
19
  rescue GitHubBub::RequestError => e
22
20
  if Request::RAISE_ON_FAIL
23
- return false
21
+ false
24
22
  else
25
23
  raise e
26
24
  end
@@ -50,4 +48,4 @@ module GitHubBub
50
48
  Request.delete(*args)
51
49
  end
52
50
  end
53
- end
51
+ end