gem_changelog_diff 0.4.0 → 0.5.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: 47e69603bad8c0641ed060fc572632c76d014dcc09f98d75322cafceefa4a1a2
4
- data.tar.gz: beb52279aac1a12e5da0e19ce981d5b7bfa497a385130de9a7503cc4ac871742
3
+ metadata.gz: 5fff27b4446b9283fde0a59b3df642289748c7551988836dfea4260f75798d2d
4
+ data.tar.gz: cd72d26b57447d9fb24e70274adeadf3955637aaf2f24c3f74d8f2d92c962eec
5
5
  SHA512:
6
- metadata.gz: e5fa45dae9504354720cafa62e7e05768b58de06d1b0376496ba3e38b74b05dc7a23b10b3ca00338acca5e38ce2f29f17495d040d508fe6f14e03515cdf33a02
7
- data.tar.gz: 537bea10e8961bff3b2d2c55530afcbe0de4fd0ab422c224b4d7b821edfe264821cb683e3d2a242d29cd551779d15a6c480e4269795347a25f190b59e56e886d
6
+ metadata.gz: 0ec391d55ff38fefd88ad0f9c18de86d7867b74357e2eeff19b0d84f160365faff36ce70ef329fac3dca75e0adf119dcb385efad884daae630e7108715b77c64
7
+ data.tar.gz: 78f6be459bd426c79c3ccc6c485a917fa5e09b6d122d08dc4f72566b49e13654d17183536f795f1a22481aede43bcaa9ae324ee6dbc25665179381105efd1377
data/CHANGELOG.md CHANGED
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.0] - 2026-06-18
11
+
12
+ ### Added
13
+
14
+ - Disk cache at `~/.cache/gem_changelog_diff/` with configurable TTL (default 24h)
15
+ - ETag conditional requests to avoid consuming rate limit on revalidation
16
+ - `cache clear` subcommand
17
+ - `--no-cache` and `--cache-ttl` flags
18
+ - Concurrent fetching via thread pool (default 4, configurable via `--concurrency`)
19
+ - Progress indicator via `tty-spinner` when running in a terminal
20
+
10
21
  ## [0.4.0] - 2026-06-18
11
22
 
12
23
  ### Added
@@ -53,7 +64,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
53
64
  - Plain text formatter for changelog output
54
65
  - Full end-to-end pipeline: detect → lookup → fetch → format
55
66
 
56
- [Unreleased]: https://github.com/eclectic-coding/gem_changelog_diff/compare/v0.4.0...HEAD
67
+ [Unreleased]: https://github.com/eclectic-coding/gem_changelog_diff/compare/v0.5.0...HEAD
68
+ [0.5.0]: https://github.com/eclectic-coding/gem_changelog_diff/releases/tag/v0.5.0
57
69
  [0.4.0]: https://github.com/eclectic-coding/gem_changelog_diff/releases/tag/v0.4.0
58
70
  [0.3.0]: https://github.com/eclectic-coding/gem_changelog_diff/releases/tag/v0.3.0
59
71
  [0.2.0]: https://github.com/eclectic-coding/gem_changelog_diff/releases/tag/v0.2.0
data/README.md CHANGED
@@ -16,6 +16,7 @@ CLI that shows you the changelog diff for each gem before you `bundle update`, p
16
16
  - [Output Control](#output-control)
17
17
  - [Detection Strategy](#detection-strategy)
18
18
  - [Filtering](#filtering)
19
+ - [Caching](#caching)
19
20
  - [Development](#development)
20
21
  - [Contributing](#contributing)
21
22
  - [License](#license)
@@ -87,6 +88,16 @@ gem_changelog_diff --group development # Filter by Bundler group
87
88
  gem_changelog_diff --ignore rails rake # Exclude specific gems
88
89
  ```
89
90
 
91
+ ### Caching
92
+
93
+ API responses are cached to `~/.cache/gem_changelog_diff/` with a 24-hour TTL. Subsequent runs reuse cached data and use ETag conditional requests to avoid consuming rate limit.
94
+
95
+ ```bash
96
+ gem_changelog_diff --no-cache # Bypass the cache
97
+ gem_changelog_diff --cache-ttl 3600 # Set TTL to 1 hour
98
+ gem_changelog_diff cache clear # Clear all cached data
99
+ ```
100
+
90
101
  [Back to top](#gemchangelogdiff)
91
102
 
92
103
  ## Development
data/ROADMAP.md CHANGED
@@ -2,21 +2,6 @@
2
2
 
3
3
  Feature roadmap for gem_changelog_diff. Each section is auto-pruned by `bin/release` when that version ships.
4
4
 
5
- ## 0.5.0 -- Caching & Performance
6
-
7
- Avoid redundant API calls. Make repeated runs fast on large dependency trees.
8
-
9
- - Disk cache at `~/.cache/gem_changelog_diff/` with configurable TTL (default 24h)
10
- - ETag conditional requests to avoid consuming rate limit on revalidation
11
- - Concurrent fetching via Ruby threads (default concurrency: 4, configurable via `--concurrency`)
12
- - Progress indicator via `tty-spinner`
13
- - `cache clear` subcommand, `--no-cache` and `--cache-ttl` flags
14
-
15
- **New files:** `cache.rb`, `concurrent_fetcher.rb`
16
- **Dependencies:** `tty-spinner` (runtime)
17
-
18
- ---
19
-
20
5
  ## 0.6.0 -- Interactive Mode & Output Formats
21
6
 
22
7
  Let users selectively browse changelogs. Support machine-readable output for CI and scripting.
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require "fileutils"
5
+ require "json"
6
+ require "net/http"
7
+
8
+ module GemChangelogDiff
9
+ class Cache
10
+ DEFAULT_DIR = File.join(Dir.home, ".cache", "gem_changelog_diff")
11
+ DEFAULT_TTL = 86_400
12
+
13
+ def initialize(cache_dir: DEFAULT_DIR, ttl: DEFAULT_TTL, enabled: true)
14
+ @cache_dir = cache_dir
15
+ @ttl = ttl
16
+ @enabled = enabled
17
+ end
18
+
19
+ def get(uri, headers: {})
20
+ return fetch_from_network(uri, headers) unless @enabled
21
+
22
+ key = cache_key(uri)
23
+ entry = read_entry(key)
24
+
25
+ if entry && fresh?(entry)
26
+ build_response(entry)
27
+ elsif entry && entry["etag"]
28
+ revalidate(uri, headers, entry, key)
29
+ else
30
+ fetch_and_store(uri, headers, key)
31
+ end
32
+ end
33
+
34
+ def clear
35
+ FileUtils.rm_rf(@cache_dir)
36
+ end
37
+
38
+ private
39
+
40
+ def cache_key(uri)
41
+ Digest::SHA256.hexdigest(uri.to_s)
42
+ end
43
+
44
+ def entry_path(key)
45
+ File.join(@cache_dir, "#{key}.json")
46
+ end
47
+
48
+ def read_entry(key)
49
+ path = entry_path(key)
50
+ return nil unless File.exist?(path)
51
+
52
+ JSON.parse(File.read(path))
53
+ rescue JSON::ParserError
54
+ nil
55
+ end
56
+
57
+ def write_entry(key, body:, code:, etag:)
58
+ FileUtils.mkdir_p(@cache_dir)
59
+ data = { "body" => body, "code" => code, "etag" => etag, "timestamp" => Time.now.to_i }
60
+ File.write(entry_path(key), JSON.generate(data))
61
+ end
62
+
63
+ def fresh?(entry)
64
+ Time.now.to_i - entry["timestamp"] < @ttl
65
+ end
66
+
67
+ def revalidate(uri, headers, entry, key)
68
+ headers = headers.merge("If-None-Match" => entry["etag"])
69
+ response = fetch_from_network(uri, headers)
70
+
71
+ if response.code == "304"
72
+ write_entry(key, body: entry["body"], code: entry["code"], etag: entry["etag"])
73
+ build_response(entry)
74
+ else
75
+ store_response(key, response)
76
+ response
77
+ end
78
+ end
79
+
80
+ def fetch_and_store(uri, headers, key)
81
+ response = fetch_from_network(uri, headers)
82
+ store_response(key, response) if response.is_a?(Net::HTTPSuccess)
83
+ response
84
+ end
85
+
86
+ def fetch_from_network(uri, headers)
87
+ request = Net::HTTP::Get.new(uri)
88
+ headers.each { |k, v| request[k] = v }
89
+
90
+ Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
91
+ http.request(request)
92
+ end
93
+ end
94
+
95
+ def store_response(key, response)
96
+ write_entry(key, body: response.body, code: response.code, etag: response["ETag"])
97
+ end
98
+
99
+ def build_response(entry)
100
+ CachedResponse.new(entry["body"], entry["code"])
101
+ end
102
+ end
103
+
104
+ class CachedResponse
105
+ attr_reader :body, :code
106
+
107
+ def initialize(body, code)
108
+ @body = body
109
+ @code = code
110
+ end
111
+
112
+ def is_a?(klass)
113
+ return true if klass == Net::HTTPSuccess && @code.start_with?("2")
114
+
115
+ super
116
+ end
117
+
118
+ def [](_header)
119
+ nil
120
+ end
121
+ end
122
+ end
@@ -9,6 +9,10 @@ module GemChangelogDiff
9
9
  FILENAMES = %w[CHANGELOG.md CHANGES.md History.md NEWS.md].freeze
10
10
  VERSION_HEADING = /^##\s+\[?v?(\d+[^\]\s]+)/
11
11
 
12
+ def initialize(cache: nil)
13
+ @cache = cache
14
+ end
15
+
12
16
  def entries_between(repo, current_version, newest_version)
13
17
  content = fetch_changelog(repo)
14
18
  return [] unless content
@@ -40,14 +44,22 @@ module GemChangelogDiff
40
44
  end
41
45
 
42
46
  def execute_request(uri)
47
+ headers = request_headers
48
+ return @cache.get(uri, headers: headers) if @cache
49
+
43
50
  request = Net::HTTP::Get.new(uri)
44
- request["Accept"] = "application/vnd.github.v3+json"
45
- request["User-Agent"] = "gem_changelog_diff/#{VERSION}"
51
+ headers.each { |k, v| request[k] = v }
52
+ Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
53
+ end
46
54
 
55
+ def request_headers
56
+ headers = {
57
+ "Accept" => "application/vnd.github.v3+json",
58
+ "User-Agent" => "gem_changelog_diff/#{VERSION}"
59
+ }
47
60
  token = GemChangelogDiff.configuration.github_token
48
- request["Authorization"] = "token #{token}" if token
49
-
50
- Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
61
+ headers["Authorization"] = "token #{token}" if token
62
+ headers
51
63
  end
52
64
 
53
65
  def parse_entries(content, current_version, newest_version)
@@ -18,6 +18,9 @@ module GemChangelogDiff
18
18
  class_option :strategy, type: :string, default: "auto", desc: "Detection strategy (auto, outdated, lockfile)"
19
19
  class_option :group, type: :string, desc: "Filter by Bundler group"
20
20
  class_option :ignore, type: :array, desc: "Gems to skip"
21
+ class_option :no_cache, type: :boolean, default: false, desc: "Disable caching"
22
+ class_option :cache_ttl, type: :numeric, desc: "Cache TTL in seconds"
23
+ class_option :concurrency, type: :numeric, default: 4, desc: "Number of concurrent fetches"
21
24
 
22
25
  desc "check [GEM...]", "Show changelog diffs for outdated gems"
23
26
  def check(*gem_names)
@@ -30,11 +33,22 @@ module GemChangelogDiff
30
33
  return
31
34
  end
32
35
 
33
- reports = build_reports(gems)
36
+ reports = with_spinner { build_reports(gems) }
34
37
  formatter = Formatter.new(color: color_enabled?)
35
38
  say formatter.format(reports)
36
39
  end
37
40
 
41
+ desc "cache SUBCOMMAND", "Manage the cache"
42
+ def cache(subcommand = nil)
43
+ case subcommand
44
+ when "clear"
45
+ Cache.new.clear
46
+ say "Cache cleared."
47
+ else
48
+ say "Usage: gem_changelog_diff cache clear"
49
+ end
50
+ end
51
+
38
52
  desc "version", "Print version"
39
53
  def version
40
54
  say "gem_changelog_diff #{VERSION}"
@@ -49,6 +63,12 @@ module GemChangelogDiff
49
63
  GemChangelogDiff.configuration.github_token = token if token
50
64
  end
51
65
 
66
+ def build_cache
67
+ ttl = options[:cache_ttl] || GemChangelogDiff.configuration.cache_ttl
68
+ enabled = !options[:no_cache] && GemChangelogDiff.configuration.cache_enabled
69
+ Cache.new(ttl: ttl, enabled: enabled)
70
+ end
71
+
52
72
  def detect_gems
53
73
  case options[:strategy]
54
74
  when "lockfile"
@@ -83,10 +103,15 @@ module GemChangelogDiff
83
103
  end
84
104
 
85
105
  def build_reports(gems)
86
- rubygems_client = RubygemsClient.new
87
- source_resolver = SourceResolver.new
88
-
89
- gems.map { |gem| build_gem_report(gem, rubygems_client, source_resolver) }
106
+ cache = build_cache
107
+ rubygems_client = RubygemsClient.new(cache: cache)
108
+ source_resolver = SourceResolver.new(
109
+ github_client: GithubClient.new(cache: cache),
110
+ changelog_parser: ChangelogParser.new(cache: cache)
111
+ )
112
+ fetcher = ConcurrentFetcher.new(concurrency: options[:concurrency])
113
+
114
+ fetcher.fetch_all(gems) { |gem| build_gem_report(gem, rubygems_client, source_resolver) }
90
115
  end
91
116
 
92
117
  def build_gem_report(gem, rubygems_client, source_resolver)
@@ -102,6 +127,18 @@ module GemChangelogDiff
102
127
  { gem: gem, releases: [], error: " #{e.message}" }
103
128
  end
104
129
 
130
+ def with_spinner
131
+ unless options[:quiet] || !$stderr.tty?
132
+ require "tty-spinner"
133
+ spinner = TTY::Spinner.new("[:spinner] Fetching changelogs...", format: :dots, output: $stderr)
134
+ spinner.auto_spin
135
+ end
136
+
137
+ result = yield
138
+ spinner&.stop("Done!")
139
+ result
140
+ end
141
+
105
142
  def color_enabled?
106
143
  !options[:no_color]
107
144
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GemChangelogDiff
4
+ class ConcurrentFetcher
5
+ def initialize(concurrency: 4)
6
+ @concurrency = concurrency
7
+ end
8
+
9
+ def fetch_all(items, &)
10
+ return items.map(&) if @concurrency <= 1
11
+
12
+ results = Array.new(items.size)
13
+ queue = Queue.new
14
+ items.each_with_index { |item, i| queue << [item, i] }
15
+
16
+ threads = spawn_workers(queue, results, &)
17
+ threads.each(&:join)
18
+ results
19
+ end
20
+
21
+ private
22
+
23
+ def spawn_workers(queue, results, &block)
24
+ worker_count = [@concurrency, queue.size].min
25
+ worker_count.times.map do
26
+ Thread.new { process_queue(queue, results, &block) }
27
+ end
28
+ end
29
+
30
+ def process_queue(queue, results, &block)
31
+ while (item, index = queue.pop(true))
32
+ results[index] = block.call(item)
33
+ end
34
+ rescue ThreadError
35
+ # Queue is empty
36
+ end
37
+ end
38
+ end
@@ -2,7 +2,12 @@
2
2
 
3
3
  module GemChangelogDiff
4
4
  class Configuration
5
- attr_accessor :github_token
5
+ attr_accessor :github_token, :cache_enabled, :cache_ttl
6
+
7
+ def initialize
8
+ @cache_enabled = true
9
+ @cache_ttl = 86_400
10
+ end
6
11
  end
7
12
 
8
13
  def self.configuration
@@ -9,6 +9,10 @@ module GemChangelogDiff
9
9
  TAG_VERSION_REGEX = /\Av?(\d+\..+)\z/
10
10
  RATE_LIMIT_WARNING_THRESHOLD = 10
11
11
 
12
+ def initialize(cache: nil)
13
+ @cache = cache
14
+ end
15
+
12
16
  def releases_between(repo, current_version, newest_version)
13
17
  releases = fetch_releases(repo)
14
18
  filter_releases(releases, current_version, newest_version)
@@ -29,17 +33,22 @@ module GemChangelogDiff
29
33
  end
30
34
 
31
35
  def execute_request(uri)
32
- request = Net::HTTP::Get.new(uri)
33
- request["Accept"] = "application/vnd.github.v3+json"
34
- request["User-Agent"] = "gem_changelog_diff/#{VERSION}"
35
- apply_auth(request)
36
+ headers = request_headers
37
+ return @cache.get(uri, headers: headers) if @cache
36
38
 
39
+ request = Net::HTTP::Get.new(uri)
40
+ headers.each { |k, v| request[k] = v }
37
41
  Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
38
42
  end
39
43
 
40
- def apply_auth(request)
44
+ def request_headers
45
+ headers = {
46
+ "Accept" => "application/vnd.github.v3+json",
47
+ "User-Agent" => "gem_changelog_diff/#{VERSION}"
48
+ }
41
49
  token = GemChangelogDiff.configuration.github_token
42
- request["Authorization"] = "token #{token}" if token
50
+ headers["Authorization"] = "token #{token}" if token
51
+ headers
43
52
  end
44
53
 
45
54
  def handle_response(response)
@@ -8,6 +8,10 @@ module GemChangelogDiff
8
8
  RUBYGEMS_API = "https://rubygems.org/api/v1/gems/%<name>s.json"
9
9
  GITHUB_REPO_REGEX = %r{github\.com/([^/]+)/([^/]+)}
10
10
 
11
+ def initialize(cache: nil)
12
+ @cache = cache
13
+ end
14
+
11
15
  def repo_url(gem_name)
12
16
  data = fetch_gem_data(gem_name)
13
17
  return nil unless data
@@ -24,7 +28,7 @@ module GemChangelogDiff
24
28
 
25
29
  def fetch_gem_data(gem_name)
26
30
  uri = URI(format(RUBYGEMS_API, name: gem_name))
27
- response = Net::HTTP.get_response(uri)
31
+ response = @cache ? @cache.get(uri) : Net::HTTP.get_response(uri)
28
32
  return nil unless response.is_a?(Net::HTTPSuccess)
29
33
 
30
34
  JSON.parse(response.body)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GemChangelogDiff
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
@@ -8,6 +8,7 @@ module GemChangelogDiff
8
8
  end
9
9
 
10
10
  require_relative "gem_changelog_diff/errors"
11
+ require_relative "gem_changelog_diff/cache"
11
12
  require_relative "gem_changelog_diff/outdated_gem"
12
13
  require_relative "gem_changelog_diff/detector"
13
14
  require_relative "gem_changelog_diff/rubygems_client"
@@ -15,5 +16,6 @@ require_relative "gem_changelog_diff/lockfile_parser"
15
16
  require_relative "gem_changelog_diff/github_client"
16
17
  require_relative "gem_changelog_diff/changelog_parser"
17
18
  require_relative "gem_changelog_diff/source_resolver"
19
+ require_relative "gem_changelog_diff/concurrent_fetcher"
18
20
  require_relative "gem_changelog_diff/formatter"
19
21
  require_relative "gem_changelog_diff/cli"
@@ -24,6 +24,41 @@ module GemChangelogDiff
24
24
 
25
25
  class Configuration
26
26
  attr_accessor github_token: String?
27
+ attr_accessor cache_enabled: bool
28
+ attr_accessor cache_ttl: Integer
29
+
30
+ def initialize: () -> void
31
+ end
32
+
33
+ class Cache
34
+ DEFAULT_DIR: String
35
+ DEFAULT_TTL: Integer
36
+
37
+ def initialize: (?cache_dir: String, ?ttl: Integer, ?enabled: bool) -> void
38
+ def get: (URI::Generic uri, ?headers: Hash[String, String]) -> (Net::HTTPResponse | CachedResponse)
39
+ def clear: () -> void
40
+
41
+ private
42
+
43
+ def cache_key: (URI::Generic uri) -> String
44
+ def entry_path: (String key) -> String
45
+ def read_entry: (String key) -> Hash[String, untyped]?
46
+ def write_entry: (String key, body: String, code: String, etag: String?) -> void
47
+ def fresh?: (Hash[String, untyped] entry) -> bool
48
+ def revalidate: (URI::Generic uri, Hash[String, String] headers, Hash[String, untyped] entry, String key) -> (Net::HTTPResponse | CachedResponse)
49
+ def fetch_and_store: (URI::Generic uri, Hash[String, String] headers, String key) -> Net::HTTPResponse
50
+ def fetch_from_network: (URI::Generic uri, Hash[String, String] headers) -> Net::HTTPResponse
51
+ def store_response: (String key, Net::HTTPResponse response) -> void
52
+ def build_response: (Hash[String, untyped] entry) -> CachedResponse
53
+ end
54
+
55
+ class CachedResponse
56
+ attr_reader body: String
57
+ attr_reader code: String
58
+
59
+ def initialize: (String body, String code) -> void
60
+ def is_a?: (Class klass) -> bool
61
+ def []: (String _header) -> nil
27
62
  end
28
63
 
29
64
  class OutdatedGem
@@ -50,6 +85,7 @@ module GemChangelogDiff
50
85
  RUBYGEMS_API: String
51
86
  GITHUB_REPO_REGEX: Regexp
52
87
 
88
+ def initialize: (?cache: Cache?) -> void
53
89
  def repo_url: (String gem_name) -> String?
54
90
  def latest_version: (String gem_name) -> String?
55
91
 
@@ -81,13 +117,15 @@ module GemChangelogDiff
81
117
  FILENAMES: Array[String]
82
118
  VERSION_HEADING: Regexp
83
119
 
120
+ def initialize: (?cache: Cache?) -> void
84
121
  def entries_between: (String repo, String current_version, String newest_version) -> Array[release_hash]
85
122
 
86
123
  private
87
124
 
88
125
  def fetch_changelog: (String repo) -> String?
89
126
  def fetch_file: (String repo, String path) -> String?
90
- def execute_request: (URI::Generic uri) -> Net::HTTPResponse
127
+ def execute_request: (URI::Generic uri) -> (Net::HTTPResponse | CachedResponse)
128
+ def request_headers: () -> Hash[String, String]
91
129
  def parse_entries: (String content, String current_version, String newest_version) -> Array[release_hash]
92
130
  def build_entry: (String version_str, String body, Gem::Version current, Gem::Version newest) -> release_hash?
93
131
  def split_sections: (String content) -> Array[[String, String]]
@@ -104,22 +142,34 @@ module GemChangelogDiff
104
142
  class GithubClient
105
143
  RELEASES_URL: String
106
144
  TAG_VERSION_REGEX: Regexp
145
+ RATE_LIMIT_WARNING_THRESHOLD: Integer
107
146
 
147
+ def initialize: (?cache: Cache?) -> void
108
148
  def releases_between: (String repo, String current_version, String newest_version) -> Array[release_hash]
109
149
 
110
150
  private
111
151
 
112
152
  def fetch_releases: (String repo) -> Array[Hash[String, untyped]]
113
- def execute_request: (URI::Generic uri) -> Net::HTTPResponse
114
- def apply_auth: (Net::HTTPRequest request) -> void
115
- def handle_response: (Net::HTTPResponse response) -> Array[Hash[String, untyped]]
116
- def check_rate_limit: (Net::HTTPResponse response) -> void
153
+ def execute_request: (URI::Generic uri) -> (Net::HTTPResponse | CachedResponse)
154
+ def request_headers: () -> Hash[String, String]
155
+ def handle_response: (Net::HTTPResponse | CachedResponse response) -> Array[Hash[String, untyped]]
156
+ def check_rate_limit: (Net::HTTPResponse | CachedResponse response) -> void
117
157
  def filter_releases: (Array[Hash[String, untyped]] releases, String current_version, String newest_version) -> Array[release_hash]
118
158
  def build_release: (Hash[String, untyped] release, Gem::Version current, Gem::Version newest) -> release_hash?
119
159
  def sort_releases: (Array[release_hash] releases) -> Array[release_hash]
120
160
  def extract_version: (String? tag) -> String?
121
161
  end
122
162
 
163
+ class ConcurrentFetcher
164
+ def initialize: (?concurrency: Integer) -> void
165
+ def fetch_all: [T, U] (Array[T], &) -> Array[U]
166
+
167
+ private
168
+
169
+ def spawn_workers: (Queue[untyped], Array[untyped], &) -> Array[Thread]
170
+ def process_queue: (Queue[untyped], Array[untyped], &) -> void
171
+ end
172
+
123
173
  type gem_report = {
124
174
  gem: OutdatedGem,
125
175
  releases: Array[release_hash],
@@ -149,13 +199,16 @@ module GemChangelogDiff
149
199
  class CLI < Thor
150
200
  def self.exit_on_failure?: () -> bool
151
201
 
152
- def check: () -> void
202
+ def check: (*String gem_names) -> void
203
+ def cache: (?String? subcommand) -> void
153
204
  def version: () -> void
154
205
 
155
206
  private
156
207
 
208
+ def with_spinner: [T] () { () -> T } -> T
157
209
  def color_enabled?: () -> bool
158
210
  def configure_token: () -> void
211
+ def build_cache: () -> Cache
159
212
  def detect_gems: () -> Array[OutdatedGem]
160
213
  def detect_with_fallback: () -> Array[OutdatedGem]
161
214
  def detect_via_lockfile: () -> Array[OutdatedGem]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_changelog_diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith
@@ -37,6 +37,20 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0.6'
40
+ - !ruby/object:Gem::Dependency
41
+ name: tty-spinner
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.9'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.9'
40
54
  description: CLI that shows you the changelog diff for each gem before you bundle
41
55
  update, pulled from GitHub releases.
42
56
  email:
@@ -57,8 +71,10 @@ files:
57
71
  - codecov.yml
58
72
  - exe/gem_changelog_diff
59
73
  - lib/gem_changelog_diff.rb
74
+ - lib/gem_changelog_diff/cache.rb
60
75
  - lib/gem_changelog_diff/changelog_parser.rb
61
76
  - lib/gem_changelog_diff/cli.rb
77
+ - lib/gem_changelog_diff/concurrent_fetcher.rb
62
78
  - lib/gem_changelog_diff/configuration.rb
63
79
  - lib/gem_changelog_diff/detector.rb
64
80
  - lib/gem_changelog_diff/errors.rb