gem_radar 0.1.0 → 0.1.1

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: 6a7c235545aa90c12bd62cc2adcc11f3ba3ed212863e4dc3b94240c1311658eb
4
- data.tar.gz: e9c2b3cff58d9a03af3af57f8e1db3c3ad8d6d1a5a4da9b09bd1114f05ca065d
3
+ metadata.gz: fd10085592b6dd19c31f335c1db17bab91f7560c872fcc2f22e04d44cff5277b
4
+ data.tar.gz: 614f4cb6f16377b96bc57f638009d3f1e80648ea3d0b5d035f7c54fafc82576d
5
5
  SHA512:
6
- metadata.gz: f893e96da321bec629f7c8c05f7533112b612c1f5bdfab3900494a130c1024c69ef3ccfdb1d0a8c8a9df3a2bfd263d97ad30dea65272f2aa77505dafa421bcab
7
- data.tar.gz: 412a70978527836e6fc4e2e20beedd010a7485c21d275e7e6ee268ca637b3535fda19c4d0bc5a4db49f3508ad376591b9e09067d2a532560f2ffcd833e2b842e
6
+ metadata.gz: 2284fb0a8557cc8b08d1a2396341874f76f5589218d454f4192f58833231afaf6dd05ab90ca4bc5d96a9b83b6b11e053ed730da8de491e905a3686485361ef98
7
+ data.tar.gz: f31e50cee88d03e1d06d3d7f286a74a67f61142257ac9b4df058bb22a3b2ff4782c1eafd06f47ef2219bd811916fc223ea247b1f04525002f16802606de44cee
@@ -0,0 +1,12 @@
1
+ ## Why
2
+
3
+ <!-- What problem does this solve, or what's the motivation? -->
4
+
5
+ ## What
6
+
7
+ <!-- What did you change, at a high level? -->
8
+
9
+ ## Evidence
10
+
11
+ <!-- Test output, logs, or screenshots showing it works (e.g. a run of
12
+ `bundle exec rspec`, or a sample report generated with your change). -->
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - uses: ruby/setup-ruby@v1
15
+ with:
16
+ bundler-cache: true
17
+
18
+ - name: Run tests
19
+ run: bundle exec rspec
20
+
21
+ lint:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+
26
+ - uses: ruby/setup-ruby@v1
27
+ with:
28
+ bundler-cache: true
29
+
30
+ - name: Run rubocop
31
+ run: bundle exec rubocop
data/.gitignore CHANGED
@@ -1,4 +0,0 @@
1
- *.md
2
- !README.md
3
- !CHANGELOG.md
4
- *.gem
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,37 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.7
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+ Exclude:
6
+ - "vendor/**/*"
7
+
8
+ Style/Documentation:
9
+ Enabled: false
10
+
11
+ Style/StringLiterals:
12
+ EnforcedStyle: double_quotes
13
+
14
+ Metrics/MethodLength:
15
+ Max: 30
16
+
17
+ Metrics/AbcSize:
18
+ Max: 46
19
+
20
+ Metrics/CyclomaticComplexity:
21
+ Max: 18
22
+
23
+ Metrics/PerceivedComplexity:
24
+ Max: 18
25
+
26
+ Metrics/ParameterLists:
27
+ Max: 6
28
+
29
+ Metrics/ModuleLength:
30
+ Max: 350
31
+
32
+ Metrics/BlockLength:
33
+ Exclude:
34
+ - "spec/**/*"
35
+
36
+ Gemspec/DevelopmentDependencies:
37
+ EnforcedStyle: Gemfile
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 4.0.6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1 (26-08-2026) - Blue Velvet
4
+
5
+ [FIX]
6
+ - A gem whose own description just mentions "deprecated" (like this one,
7
+ which describes detecting deprecated gems) no longer flags itself as deprecated. Only actual deprecation notices are detected now.
8
+
9
+ [INTERNAL]
10
+ - Only test in current ruby version.
11
+ - Lowered `required_ruby_version` to `>= 2.7.0` so the gem can be added to
12
+ older projects. Verified with a new spec (`spec/ruby_compatibility_spec.rb`) that shells out to every installed Ruby version from 2.7 up and confirms the library loads correctly.
13
+
3
14
  ## 0.1.0 (26-08-2026) - Brazil
4
15
 
5
16
  Initial public release.
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ group :development do
8
+ gem "rake", "~> 13.0"
9
+ gem "rspec", "~> 3.13"
10
+ gem "rubocop", "~> 1.75"
11
+ gem "webmock", "~> 3.19"
12
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,113 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gem_radar (0.1.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ addressable (2.9.0)
10
+ public_suffix (>= 2.0.2, < 8.0)
11
+ ast (2.4.3)
12
+ bigdecimal (4.1.2)
13
+ crack (1.0.1)
14
+ bigdecimal
15
+ rexml
16
+ diff-lcs (1.6.2)
17
+ hashdiff (1.2.1)
18
+ json (2.21.2)
19
+ language_server-protocol (3.17.0.6)
20
+ lint_roller (1.1.0)
21
+ parallel (2.1.0)
22
+ parser (3.3.12.0)
23
+ ast (~> 2.4.1)
24
+ racc
25
+ prism (1.9.0)
26
+ public_suffix (7.0.5)
27
+ racc (1.8.1)
28
+ rainbow (3.1.1)
29
+ rake (13.4.2)
30
+ regexp_parser (2.12.0)
31
+ rexml (3.4.4)
32
+ rspec (3.13.2)
33
+ rspec-core (~> 3.13.0)
34
+ rspec-expectations (~> 3.13.0)
35
+ rspec-mocks (~> 3.13.0)
36
+ rspec-core (3.13.6)
37
+ rspec-support (~> 3.13.0)
38
+ rspec-expectations (3.13.5)
39
+ diff-lcs (>= 1.2.0, < 2.0)
40
+ rspec-support (~> 3.13.0)
41
+ rspec-mocks (3.13.8)
42
+ diff-lcs (>= 1.2.0, < 2.0)
43
+ rspec-support (~> 3.13.0)
44
+ rspec-support (3.13.7)
45
+ rubocop (1.90.0)
46
+ json (>= 2.3)
47
+ language_server-protocol (~> 3.17.0.2)
48
+ lint_roller (~> 1.1.0)
49
+ parallel (>= 1.10)
50
+ parser (>= 3.3.0.2)
51
+ rainbow (>= 2.2.2, < 4.0)
52
+ regexp_parser (>= 2.9.3, < 3.0)
53
+ rubocop-ast (>= 1.49.0, < 2.0)
54
+ ruby-progressbar (~> 1.7)
55
+ unicode-display_width (>= 2.4.0, < 4.0)
56
+ rubocop-ast (1.50.0)
57
+ parser (>= 3.3.7.2)
58
+ prism (~> 1.7)
59
+ ruby-progressbar (1.13.0)
60
+ unicode-display_width (3.2.0)
61
+ unicode-emoji (~> 4.1)
62
+ unicode-emoji (4.2.0)
63
+ webmock (3.26.3)
64
+ addressable (>= 2.8.0)
65
+ crack (>= 0.3.2)
66
+ hashdiff (>= 0.4.0, < 2.0.0)
67
+
68
+ PLATFORMS
69
+ ruby
70
+ x86_64-linux
71
+
72
+ DEPENDENCIES
73
+ gem_radar!
74
+ rake (~> 13.0)
75
+ rspec (~> 3.13)
76
+ rubocop (~> 1.75)
77
+ webmock (~> 3.19)
78
+
79
+ CHECKSUMS
80
+ addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
81
+ ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
82
+ bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
83
+ bundler (4.0.18) sha256=02d9a17429de1847b4e0c9f27a9ee4b20c0a74c0a641b4e77195d6019e3618ac
84
+ crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
85
+ diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
86
+ gem_radar (0.1.1)
87
+ hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
88
+ json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
89
+ language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
90
+ lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
91
+ parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
92
+ parser (3.3.12.0) sha256=21a6d7f755d5a24dfbdc6e6b772e4e879a52e7631a88bc5a3a134606052c9828
93
+ prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
94
+ public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
95
+ racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
96
+ rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
97
+ rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
98
+ regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
99
+ rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
100
+ rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
101
+ rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
102
+ rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
103
+ rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
104
+ rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
105
+ rubocop (1.90.0) sha256=9eb4c065b5c5154e4ef554c547972f3905a9eb6b53e657e580b6796b54bf8242
106
+ rubocop-ast (1.50.0) sha256=b9ca88300da0803ee222ad20cdb30494c0a784eed06fdc35d254b06d662788db
107
+ ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
108
+ unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
109
+ unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
110
+ webmock (3.26.3) sha256=77c612816bdcb2fc43db0e42fd98b19be7d4770fd708816fdde65a006664ddec
111
+
112
+ BUNDLED WITH
113
+ 4.0.18
data/README.md CHANGED
@@ -8,6 +8,16 @@ compatibility and update status.
8
8
  For each gem, the report links only to its repository — it does not
9
9
  download or embed changelog content.
10
10
 
11
+ ## Ruby compatibility
12
+
13
+ Works on **>= Ruby 2.7**.
14
+
15
+ No runtime dependencies means adding `gem_radar` to an old project's `Gemfile` doesn't drag in anything that would clash with it.
16
+
17
+ This is **deliberate**: the projects that most need help
18
+ figuring out an upgrade path are exactly the ones still stuck on an old RoR apps.
19
+
20
+
11
21
  ## Installation
12
22
 
13
23
  Add it to your project's `Gemfile`:
@@ -73,6 +83,33 @@ whether a gem's repository has been archived.
73
83
 
74
84
  Ruby standard library only — no runtime dependencies.
75
85
 
86
+ ## How to contribute
87
+
88
+ ```sh
89
+ git clone https://github.com/laurajaime/gem_radar.git
90
+ cd gem_radar
91
+ bundle install
92
+ ```
93
+
94
+ Run the test suite:
95
+
96
+ ```sh
97
+ bundle exec rspec
98
+ ```
99
+
100
+ Run the linter:
101
+
102
+ ```sh
103
+ bundle exec rubocop
104
+ ```
105
+
106
+ Both run in CI on every push and pull request (see
107
+ [.github/workflows/ci.yml](.github/workflows/ci.yml)).
108
+
109
+ Open your PR explaining **why** the change is needed and **what** it does,
110
+ with evidence that it works (test output, logs, or screenshots) — see the
111
+ [PR template](.github/PULL_REQUEST_TEMPLATE.md).
112
+
76
113
  ## License
77
114
 
78
115
  MIT
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
data/gem_radar.gemspec CHANGED
@@ -9,13 +9,14 @@ Gem::Specification.new do |spec|
9
9
 
10
10
  spec.summary = "Audits your Ruby/Rails project's gems and reports their compatibility and update status."
11
11
  spec.description = "Reads a project's Gemfile.lock and reports, for each direct gem, whether it's " \
12
- "compatible with the project's Ruby/Rails, needs a mandatory update, is " \
13
- "deprecated or archived, or can be upgraded safely vs. with breaking changes."
12
+ "compatible with the project's Ruby/Rails, needs a mandatory update, is " \
13
+ "deprecated or archived, or can be upgraded safely vs. with breaking changes."
14
14
  spec.homepage = "https://github.com/laurajaime/gem_radar"
15
15
  spec.license = "MIT"
16
- spec.required_ruby_version = ">= 3.0.0"
16
+ spec.required_ruby_version = ">= 2.7.0"
17
17
 
18
18
  spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
19
+ spec.metadata["rubygems_mfa_required"] = "true"
19
20
 
20
21
  spec.files = Dir.chdir(__dir__) do
21
22
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
data/lib/gem_radar/cli.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  #
3
4
  # gem_radar — generates a Markdown report on the health of a Ruby/Rails
4
5
  # project's direct gems, grouped into categories:
@@ -45,14 +46,17 @@ module GemRadar
45
46
  lock = parse_lockfile(options[:lockfile])
46
47
  ruby_version = detect_ruby_version(options, lock)
47
48
  rails_version = detect_rails_version(options, lock)
48
- project = File.basename(File.expand_path(File.dirname(options[:lockfile]) == "." ? Dir.pwd : File.dirname(options[:lockfile])))
49
+ lockfile_dir = File.dirname(options[:lockfile])
50
+ project = File.basename(File.expand_path(lockfile_dir == "." ? Dir.pwd : lockfile_dir))
49
51
 
50
52
  warn "Project: #{project} · Ruby: #{ruby_version || 'not detected (use --ruby)'} · " \
51
53
  "Rails: #{rails_version || 'not detected (use --rails)'} · #{lock[:dependencies].size} direct gems"
52
- warn "Warning: no GITHUB_TOKEN and no authenticated `gh`; GitHub's API is limited to 60 requests/hour." unless github_token
54
+ unless github_token
55
+ warn "Warning: no GITHUB_TOKEN and no authenticated `gh`; GitHub's API is limited to 60 requests/hour."
56
+ end
53
57
 
54
58
  results = lock[:dependencies].each_with_index.map do |name, i|
55
- warn format(" [%d/%d] %s…", i + 1, lock[:dependencies].size, name)
59
+ warn format(" [%<index>d/%<total>d] %<name>s…", index: i + 1, total: lock[:dependencies].size, name: name)
56
60
  process_gem(name, lock[:specs][name], ruby_version, rails_version)
57
61
  end
58
62
 
@@ -69,12 +73,22 @@ module GemRadar
69
73
  options = { output: "gem_radar.md", lockfile: "Gemfile.lock" }
70
74
  OptionParser.new do |o|
71
75
  o.banner = "Usage: gem_radar [options] (run from inside the project directory)"
72
- o.on("--ruby VERSION", "Project's Ruby version (default: .ruby-version or Gemfile.lock)") { |v| options[:ruby] = v }
73
- o.on("--rails VERSION", "Project's Rails version (default: the one in Gemfile.lock)") { |v| options[:rails] = v }
76
+ o.on("--ruby VERSION", "Project's Ruby version (default: .ruby-version or Gemfile.lock)") do |v|
77
+ options[:ruby] = v
78
+ end
79
+ o.on("--rails VERSION", "Project's Rails version (default: the one in Gemfile.lock)") do |v|
80
+ options[:rails] = v
81
+ end
74
82
  o.on("--lockfile PATH", "Path to Gemfile.lock (default: ./Gemfile.lock)") { |v| options[:lockfile] = v }
75
83
  o.on("-o", "--output PATH", "Output file (default: gem_radar.md)") { |v| options[:output] = v }
76
- o.on("-h", "--help", "Shows this help") { puts o; exit }
77
- o.on("--version", "Shows the version") { puts "gem_radar #{VERSION} (\"#{CODENAME}\")"; exit }
84
+ o.on("-h", "--help", "Shows this help") do
85
+ puts o
86
+ exit
87
+ end
88
+ o.on("--version", "Shows the version") do
89
+ puts "gem_radar #{VERSION} (\"#{CODENAME}\")"
90
+ exit
91
+ end
78
92
  end.parse!(argv)
79
93
  options
80
94
  end
@@ -83,12 +97,13 @@ module GemRadar
83
97
 
84
98
  def http_get(url, headers = {}, limit = 5)
85
99
  return nil if limit.zero?
100
+
86
101
  uri = URI(url)
87
102
  req = Net::HTTP::Get.new(uri)
88
103
  req["User-Agent"] = USER_AGENT
89
104
  headers.each { |k, v| req[k] = v }
90
105
  res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https",
91
- open_timeout: 10, read_timeout: 30) { |h| h.request(req) }
106
+ open_timeout: 10, read_timeout: 30) { |h| h.request(req) }
92
107
  if res.is_a?(Net::HTTPRedirection) && res["location"]
93
108
  http_get(URI.join(url, res["location"]).to_s, headers, limit - 1)
94
109
  else
@@ -101,7 +116,8 @@ module GemRadar
101
116
 
102
117
  def github_token
103
118
  return @github_token if defined?(@github_token)
104
- token = ENV["GITHUB_TOKEN"] || ENV["GH_TOKEN"]
119
+
120
+ token = ENV["GITHUB_TOKEN"] || ENV.fetch("GH_TOKEN", nil)
105
121
  token ||= begin
106
122
  out = `gh auth token 2>/dev/null`.strip
107
123
  out.empty? ? nil : out
@@ -116,6 +132,7 @@ module GemRadar
116
132
  headers["Authorization"] = "Bearer #{github_token}" if github_token
117
133
  res = http_get("https://api.github.com#{path}", headers)
118
134
  return nil unless res.is_a?(Net::HTTPSuccess)
135
+
119
136
  JSON.parse(res.body)
120
137
  rescue JSON::ParserError
121
138
  nil
@@ -137,11 +154,11 @@ module GemRadar
137
154
  end
138
155
  case section
139
156
  when "GEM", "GIT", "PATH"
140
- specs[$1] = $2 if line =~ /\A (\S+) \(([^)\s]+)\)/
157
+ specs[::Regexp.last_match(1)] = ::Regexp.last_match(2) if line =~ /\A (\S+) \(([^)\s]+)\)/
141
158
  when "DEPENDENCIES"
142
- deps << $1 if line =~ /\A ([^\s!(]+)/
159
+ deps << ::Regexp.last_match(1) if line =~ /\A ([^\s!(]+)/
143
160
  when "RUBY VERSION"
144
- ruby = $1 if line =~ /ruby (\d+\.\d+\.\d+)/
161
+ ruby = ::Regexp.last_match(1) if line =~ /ruby (\d+\.\d+\.\d+)/
145
162
  end
146
163
  end
147
164
  { dependencies: deps.uniq.sort, specs: specs, ruby: ruby }
@@ -149,6 +166,7 @@ module GemRadar
149
166
 
150
167
  def detect_ruby_version(options, lockfile_data)
151
168
  return options[:ruby] if options[:ruby]
169
+
152
170
  if File.file?(".ruby-version")
153
171
  v = File.read(".ruby-version").strip.sub(/\Aruby-/, "")
154
172
  return v unless v.empty?
@@ -165,6 +183,7 @@ module GemRadar
165
183
  def rubygems_info(name)
166
184
  res = http_get("https://rubygems.org/api/v1/gems/#{name}.json")
167
185
  return nil unless res.is_a?(Net::HTTPSuccess)
186
+
168
187
  JSON.parse(res.body)
169
188
  rescue JSON::ParserError
170
189
  nil
@@ -173,6 +192,7 @@ module GemRadar
173
192
  def rubygems_versions(name)
174
193
  res = http_get("https://rubygems.org/api/v1/versions/#{name}.json")
175
194
  return [] unless res.is_a?(Net::HTTPSuccess)
195
+
176
196
  JSON.parse(res.body)
177
197
  rescue JSON::ParserError
178
198
  []
@@ -182,6 +202,7 @@ module GemRadar
182
202
  @version_deps_cache ||= {}
183
203
  key = "#{name}@#{version}"
184
204
  return @version_deps_cache[key] if @version_deps_cache.key?(key)
205
+
185
206
  res = http_get("https://rubygems.org/api/v2/rubygems/#{name}/versions/#{version}.json")
186
207
  @version_deps_cache[key] =
187
208
  begin
@@ -199,6 +220,7 @@ module GemRadar
199
220
 
200
221
  def requirement_satisfied?(requirement_str, version)
201
222
  return true if requirement_str.nil? || requirement_str.to_s.empty?
223
+
202
224
  Gem::Requirement.new(*requirement_str.split(",").map(&:strip)).satisfied_by?(version)
203
225
  rescue StandardError
204
226
  true
@@ -210,8 +232,10 @@ module GemRadar
210
232
 
211
233
  def rails_requirement_satisfied?(runtime_deps, rails_version)
212
234
  return true unless rails_version
235
+
213
236
  rails_deps = Array(runtime_deps).select { |d| RAILS_FAMILY.include?(d["name"]) }
214
237
  return true if rails_deps.empty?
238
+
215
239
  rails_deps.all? { |d| requirement_satisfied?(d["requirements"], rails_version) }
216
240
  end
217
241
 
@@ -248,6 +272,7 @@ module GemRadar
248
272
  # not just a recommendation (unlike the "updatable" categories).
249
273
  def mandatory_update_reasons(name, installed, versions, ruby_version, rails_version, latest_runtime_deps)
250
274
  return [] unless installed
275
+
251
276
  entry = versions.find { |v| gem_version(v["number"]) == installed }
252
277
  return [] unless entry # installed version not indexed on rubygems.org (yanked, etc.)
253
278
 
@@ -280,6 +305,7 @@ module GemRadar
280
305
 
281
306
  def github_repo(url)
282
307
  return nil unless url && !url.empty?
308
+
283
309
  m = url.match(%r{github\.com/([^/]+)/([^/#?]+)})
284
310
  m && "#{m[1]}/#{m[2].sub(/\.git\z/, '')}"
285
311
  end
@@ -294,10 +320,24 @@ module GemRadar
294
320
  [info["source_code_uri"] || info["homepage_uri"], nil]
295
321
  end
296
322
 
323
+ # Matches an actual deprecation notice ("DEPRECATED: ...", "this gem is
324
+ # deprecated", "no longer maintained"), not just any mention of the word
325
+ # — a gem *about* deprecation (like this one) can otherwise flag itself.
326
+ DEPRECATION_NOTICE = /
327
+ (?:\A|[.!]\s*) \W*
328
+ (?: this \s gem \s (?:is|has \s been) \s deprecated
329
+ | deprecated \s*[:-]
330
+ | \[deprecated\]
331
+ | no \s longer \s maintained
332
+ | unmaintained
333
+ )
334
+ /ix.freeze
335
+
297
336
  def deprecated?(info, repo_info)
298
337
  return true if repo_info && repo_info["archived"]
338
+
299
339
  text = "#{info['info']} #{info['description']}"
300
- !!(text =~ /\bdeprecat/i)
340
+ !!(text =~ DEPRECATION_NOTICE)
301
341
  end
302
342
 
303
343
  # --- Classification -----------------------------------------------------------
@@ -323,15 +363,17 @@ module GemRadar
323
363
  end
324
364
  end
325
365
 
326
- def classify(r)
327
- return :not_found unless r[:found]
328
- return :mandatory if r[:mandatory_reasons] && !r[:mandatory_reasons].empty?
329
- return :deprecated if r[:deprecated]
330
- return :compatible unless r[:installed] && r[:latest]
366
+ def classify(result)
367
+ return :not_found unless result[:found]
368
+ return :mandatory if result[:mandatory_reasons] && !result[:mandatory_reasons].empty?
369
+ return :deprecated if result[:deprecated]
370
+ return :compatible unless result[:installed] && result[:latest]
331
371
 
332
- if r[:compatible] && r[:compatible] > r[:installed]
333
- same_compat_line?(r[:installed], r[:compatible]) ? :updatable_safe : :updatable_breaking
334
- elsif r[:latest] > r[:installed] && (r[:compatible].nil? || r[:compatible] <= r[:installed])
372
+ no_compatible_update = result[:compatible].nil? || result[:compatible] <= result[:installed]
373
+
374
+ if result[:compatible] && result[:compatible] > result[:installed]
375
+ same_compat_line?(result[:installed], result[:compatible]) ? :updatable_safe : :updatable_breaking
376
+ elsif result[:latest] > result[:installed] && no_compatible_update
335
377
  # A newer version exists, but none was found compatible with the
336
378
  # current Ruby/Rails: updating means upgrading the framework too.
337
379
  :updatable_breaking
@@ -342,24 +384,24 @@ module GemRadar
342
384
 
343
385
  # --- Report -------------------------------------------------------------------
344
386
 
345
- def format_gem_line(r)
346
- parts = ["**#{r[:name]}**"]
347
- parts << "installed `#{r[:installed]}`" if r[:installed]
348
- if r[:compatible] && r[:installed] && r[:compatible] > r[:installed]
349
- parts << "recommended `#{r[:compatible]}`"
387
+ def format_gem_line(result)
388
+ parts = ["**#{result[:name]}**"]
389
+ parts << "installed `#{result[:installed]}`" if result[:installed]
390
+ if result[:compatible] && result[:installed] && result[:compatible] > result[:installed]
391
+ parts << "recommended `#{result[:compatible]}`"
350
392
  end
351
- if r[:latest] && (r[:compatible].nil? || r[:latest] > r[:compatible])
352
- parts << "latest published `#{r[:latest]}`"
393
+ if result[:latest] && (result[:compatible].nil? || result[:latest] > result[:compatible])
394
+ parts << "latest published `#{result[:latest]}`"
353
395
  end
354
396
  line = "- #{parts.join(' · ')}"
355
- line += " — [repository](#{r[:repo_url]})" if r[:repo_url]
356
- line += "\n\n > #{r[:error]}" if r[:error]
357
- Array(r[:mandatory_reasons]).each { |reason| line += "\n\n > #{reason}" }
397
+ line += " — [repository](#{result[:repo_url]})" if result[:repo_url]
398
+ line += "\n\n > #{result[:error]}" if result[:error]
399
+ Array(result[:mandatory_reasons]).each { |reason| line += "\n\n > #{reason}" }
358
400
  line
359
401
  end
360
402
 
361
- def build_report(project, ruby_version, rails_version, results, options)
362
- grouped = results.group_by { |r| classify(r) }
403
+ def build_report(project, ruby_version, rails_version, results, _options)
404
+ grouped = results.group_by { |result| classify(result) }
363
405
 
364
406
  lines = []
365
407
  lines << "# Gems in #{project}"
@@ -381,12 +423,13 @@ module GemRadar
381
423
  CATEGORY_ORDER.each do |cat|
382
424
  items = grouped[cat]
383
425
  next if items.nil? || items.empty?
426
+
384
427
  lines << ""
385
428
  lines << "## #{CATEGORY_TITLES[cat]}"
386
429
  lines << ""
387
- items.each { |r| lines << format_gem_line(r) }
430
+ items.each { |result| lines << format_gem_line(result) }
388
431
  end
389
- lines.join("\n") + "\n"
432
+ "#{lines.join("\n")}\n"
390
433
  end
391
434
 
392
435
  # --- Per-gem processing ------------------------------------------------------
@@ -403,7 +446,8 @@ module GemRadar
403
446
  versions = rubygems_versions(name)
404
447
  latest_runtime_deps = info.dig("dependencies", "runtime") || []
405
448
  compatible = latest_compatible(name, versions, ruby_version, rails_version, latest_runtime_deps)
406
- mandatory_reasons = mandatory_update_reasons(name, installed, versions, ruby_version, rails_version, latest_runtime_deps)
449
+ mandatory_reasons = mandatory_update_reasons(name, installed, versions, ruby_version, rails_version,
450
+ latest_runtime_deps)
407
451
 
408
452
  url, repo = repo_url_for(info)
409
453
  repo_info = repo && github_api("/repos/#{repo}")
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GemRadar
4
- VERSION = "0.1.0"
5
- CODENAME = "Brazil"
4
+ VERSION = "0.1.1"
5
+ CODENAME = "Blue Velvet"
6
6
  end
metadata CHANGED
@@ -1,28 +1,34 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_radar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laura Jaime
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2026-08-26 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: Reads a project's Gemfile.lock and reports, for each direct gem, whether
14
13
  it's compatible with the project's Ruby/Rails, needs a mandatory update, is deprecated
15
14
  or archived, or can be upgraded safely vs. with breaking changes.
16
- email:
17
15
  executables:
18
16
  - gem_radar
19
17
  extensions: []
20
18
  extra_rdoc_files: []
21
19
  files:
20
+ - ".github/PULL_REQUEST_TEMPLATE.md"
21
+ - ".github/workflows/ci.yml"
22
22
  - ".gitignore"
23
+ - ".rspec"
24
+ - ".rubocop.yml"
25
+ - ".ruby-version"
23
26
  - CHANGELOG.md
27
+ - Gemfile
28
+ - Gemfile.lock
24
29
  - LICENSE
25
30
  - README.md
31
+ - Rakefile
26
32
  - assets/banner.png
27
33
  - exe/gem_radar
28
34
  - gem_radar.gemspec
@@ -34,7 +40,7 @@ licenses:
34
40
  - MIT
35
41
  metadata:
36
42
  changelog_uri: https://github.com/laurajaime/gem_radar/blob/main/CHANGELOG.md
37
- post_install_message:
43
+ rubygems_mfa_required: 'true'
38
44
  rdoc_options: []
39
45
  require_paths:
40
46
  - lib
@@ -42,15 +48,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
42
48
  requirements:
43
49
  - - ">="
44
50
  - !ruby/object:Gem::Version
45
- version: 3.0.0
51
+ version: 2.7.0
46
52
  required_rubygems_version: !ruby/object:Gem::Requirement
47
53
  requirements:
48
54
  - - ">="
49
55
  - !ruby/object:Gem::Version
50
56
  version: '0'
51
57
  requirements: []
52
- rubygems_version: 3.3.7
53
- signing_key:
58
+ rubygems_version: 4.0.16
54
59
  specification_version: 4
55
60
  summary: Audits your Ruby/Rails project's gems and reports their compatibility and
56
61
  update status.