gem_sorter 0.5.0 → 0.5.3

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: 70fd0cee6472698ca2168aa26c7db2524a0c678cdb0b4270010653026d5729e7
4
- data.tar.gz: 3a9760914476b821fc87a3cb15293b7d90ebb6b3044b37520d4fb078d634b4ea
3
+ metadata.gz: e7eb0be5f76f0fe5bf85c828b98e7bc0aa2fedbab8131a0db6aefbd8664bfbf8
4
+ data.tar.gz: 893bb62c2c6e21857660a911c649c88c244f3eb3d8dcb2fdc9b8274e535bc26b
5
5
  SHA512:
6
- metadata.gz: d4aaa1957dc88ff452f8d799aaa0468646e5f11972975edadf04c3f75f41efd3a620d28fbdc76b56140ded25e99a9be619b6281c521706315ea0a49858a7f1e9
7
- data.tar.gz: 8b8a3eed7eba14a635b7153c2dbc4534161763d4a8db999110e55346b5b586aebf1eac692eaf928c1bacd1bdb584c68153eefa28110c142f90e2e986d36f606a
6
+ metadata.gz: b412dd7b89dd9a53115ec289b4010e7da6756b792e7cd16940fb0e06c53fbf44e96456d24cd84d0a9fa204babd2775252864a688382dfcb69baee4a3b44cbe92
7
+ data.tar.gz: e3b8fb028356f3c2de88c4c4bebde010325f8dc3b8d38bc1887c2c4ddf8c4f0f1162e3c86f9ea81992ccd7f88d9255e0f86b49089e6102f00e8151e6402360b3
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
  <img src="https://i.imgur.com/WHOyL9W.png" width="150" alt="GemSorter Logo">
3
3
  </p>
4
4
 
5
+ [![Gem Version](https://badge.fury.io/rb/gem_sorter.svg)](https://badge.fury.io/rb/gem_sorter)
6
+
5
7
  # GemSorter
6
8
 
7
9
  GemSorter is a simple gem to sort the contents of your Gemfile alphabetically while preserving comments and group structure. It helps maintain a clean and organized Gemfile.
@@ -44,6 +46,7 @@ rake -r gem_sorter gemfile:sort
44
46
  * `backup`: Pass `true` to create a backup of your Gemfile as `Gemfile.old` before sorting.
45
47
  * `update_comments`: Pass `true` to update the comments of the gems based on their descriptions.
46
48
  * `update_versions`: Pass `true` to update the versions of the gems based on the lockfile.
49
+ * `force_update`: Pass `true` to update gems to their latest available versions from RubyGems, ignoring the lockfile. This will print a summary of updated gems and remind you to run `bundle install`.
47
50
  * `use_double_quotes`: Pass `true` to convert single quotes to double quotes in gem declarations.
48
51
  * `remove_versions`: Pass `true` to remove version constraints from gems while preserving other parameters like `require` or `platforms`. This option takes precedence over `update_versions` if both are enabled.
49
52
 
@@ -59,6 +62,7 @@ Create a file in the root of your project called `gem_sorter.yml`
59
62
  * `backup`: Pass `true` to create a backup of your Gemfile as `Gemfile.old` before sorting.
60
63
  * `update_comments`: Pass `true` to update the comments of the gems based on their descriptions.
61
64
  * `update_versions`: Pass `true` to update the versions of the gems based on the lockfile.
65
+ * `force_update`: Pass `true` to update gems to their latest available versions from RubyGems, ignoring the lockfile. This will print a summary of updated gems and remind you to run `bundle install`.
62
66
  * `use_double_quotes`: Pass `true` to convert single quotes to double quotes in gem declarations.
63
67
  * `remove_versions`: Pass `true` to remove version constraints from gems while preserving other parameters like `require` or `platforms`. This option takes precedence over `update_versions` if both are enabled.
64
68
  * `ignore_gems`: Pass an array of GEMs you want to ignore versions and comments
@@ -71,6 +75,7 @@ Example:
71
75
  backup: true
72
76
  update_comments: true
73
77
  update_versions: false
78
+ force_update: false
74
79
  use_double_quotes: true
75
80
  remove_versions: true
76
81
  ignore_gems:
@@ -1,3 +1,3 @@
1
1
  module GemSorter
2
- VERSION = '0.5.0'
2
+ VERSION = '0.5.3'
3
3
  end
data/lib/gem_sorter.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # lib/gem_sorter.rb
2
2
  require 'net/http'
3
3
  require 'cgi'
4
+ require 'openssl'
5
+ require 'json'
4
6
 
5
7
  load File.expand_path('tasks/gem_sorter.rake', __dir__) if defined?(Rake)
6
8
 
@@ -11,22 +13,33 @@ module GemSorter
11
13
  @filepath = task_config.gemfile_path
12
14
  @content = File.read(task_config.gemfile_path)
13
15
  @versions = nil
16
+ @version_updates = []
14
17
  end
15
18
 
16
19
  def sort
20
+ ruby_line = extract_ruby_line(@content)
21
+
17
22
  parts = @content.split(/^group/)
18
23
  main_section = parts.shift
19
24
  group_sections = parts
20
25
 
21
- source_line, gems = process_main_section(main_section)
26
+ source_line, gems, _ = process_main_section(main_section)
22
27
 
23
28
  update_gem_summaries(gems) if @config.update_comments
24
- update_version_text(gems) if @config.update_versions
29
+ if @config.force_update
30
+ force_update_versions(gems)
31
+ elsif @config.update_versions
32
+ update_version_text(gems)
33
+ end
25
34
  remove_versions(gems) if @config.remove_versions
26
35
  sorted_gems = sort_gem_blocks(gems)
27
36
 
28
37
  result = []
29
38
  result << source_line
39
+ if ruby_line
40
+ result << ''
41
+ result << ruby_line
42
+ end
30
43
  result << ''
31
44
  result.concat(sorted_gems)
32
45
  result << ''
@@ -34,7 +47,11 @@ module GemSorter
34
47
  group_sections.each do |section|
35
48
  group_gems = process_group_section(section)
36
49
  update_gem_summaries(group_gems) if @config.update_comments
37
- update_version_text(group_gems) if @config.update_versions
50
+ if @config.force_update
51
+ force_update_versions(group_gems)
52
+ elsif @config.update_versions
53
+ update_version_text(group_gems)
54
+ end
38
55
  remove_versions(group_gems) if @config.remove_versions
39
56
  result << "group#{section.split("\n").first}"
40
57
  result.concat(sort_gem_blocks(group_gems).map { |line| " #{line}" })
@@ -44,11 +61,19 @@ module GemSorter
44
61
 
45
62
  result = transform_to_double_quotes(result) if @config.use_double_quotes
46
63
 
64
+ print_version_updates if @config.force_update && !@version_updates.empty?
65
+
47
66
  result.join("\n")
48
67
  end
49
68
 
50
69
  private
51
70
 
71
+ def extract_ruby_line(content)
72
+ lines = content.split("\n")
73
+ ruby_line = lines.find { |line| line.strip.start_with?('ruby') }
74
+ ruby_line&.strip
75
+ end
76
+
52
77
  def transform_to_double_quotes(gem_file_content)
53
78
  return gem_file_content unless gem_file_content.is_a?(Array) || gem_file_content.is_a?(String)
54
79
 
@@ -100,6 +125,43 @@ module GemSorter
100
125
  end
101
126
  end
102
127
 
128
+ def force_update_versions(gems)
129
+ @versions ||= fetch_versions_from_lockfile("#{@filepath}.lock")
130
+
131
+ gems.each do |gem_block|
132
+ gem_name = extract_gem_name(gem_block[:gem_line])
133
+ next if @config.ignore_gems.include?(gem_name) || @config.ignore_gem_versions.include?(gem_name)
134
+
135
+ # Try to get current version from lockfile first, then from gem line
136
+ current_version = @versions[gem_name] || extract_current_version(gem_block[:gem_line])
137
+ latest_version = fetch_latest_version(gem_name)
138
+
139
+ next unless latest_version
140
+
141
+ extra_params = extract_params(gem_block[:gem_line])
142
+ new_gemfile_text = fetch_gemfile_text(gem_name, latest_version, gem_block[:gem_line])
143
+
144
+ next unless new_gemfile_text && new_gemfile_text != gem_block[:gem_line]
145
+
146
+ gem_block[:gem_line] = [new_gemfile_text.strip, extra_params].select { |value| !value.nil? && !value.empty? }.join(',')
147
+
148
+ # Track version update if version changed
149
+ # Compare semantic versions (x.y.z) for accurate comparison
150
+ current_semantic = current_version&.match(/(\d+\.\d+\.\d+)/)
151
+ current_semantic = current_semantic ? current_semantic[1] : nil
152
+ latest_semantic = latest_version.match(/(\d+\.\d+\.\d+)/)
153
+ latest_semantic = latest_semantic ? latest_semantic[1] : nil
154
+
155
+ if current_semantic != latest_semantic
156
+ @version_updates << {
157
+ gem_name: gem_name,
158
+ from_version: current_version || 'no version specified',
159
+ to_version: latest_version
160
+ }
161
+ end
162
+ end
163
+ end
164
+
103
165
  def remove_versions(gems)
104
166
  gems.each do |gem_block|
105
167
  gem_name = extract_gem_name(gem_block[:gem_line])
@@ -147,11 +209,14 @@ module GemSorter
147
209
  source_line = lines.shift
148
210
 
149
211
  gems = []
212
+ ruby_line = nil
150
213
  current_comments = []
151
214
 
152
215
  lines.each do |line|
153
216
  if line.start_with?('#')
154
217
  current_comments << line
218
+ elsif line.start_with?('ruby')
219
+ ruby_line = line
155
220
  elsif line.start_with?('gem')
156
221
  gems << {
157
222
  comments: current_comments,
@@ -161,7 +226,7 @@ module GemSorter
161
226
  end
162
227
  end
163
228
 
164
- [source_line, gems]
229
+ [source_line, gems, ruby_line]
165
230
  end
166
231
 
167
232
  def process_group_section(section)
@@ -215,7 +280,7 @@ module GemSorter
215
280
  return get_summary(gem_name, true) unless remote
216
281
  nil
217
282
  end
218
- rescue StandardError => e
283
+ rescue StandardError
219
284
  nil
220
285
  end
221
286
 
@@ -224,11 +289,18 @@ module GemSorter
224
289
  url = URI(version ? "#{base_url}/versions/#{version.strip}" : base_url)
225
290
 
226
291
  begin
227
- response = Net::HTTP.get(url)
228
- unless response
229
- raise "Error: Could not fetch gem information from RubyGems for #{gem_name} version #{version}."
292
+ http = Net::HTTP.new(url.host, url.port)
293
+ http.use_ssl = true
294
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
295
+
296
+ request = Net::HTTP::Get.new(url)
297
+ response = http.request(request)
298
+
299
+ unless response.is_a?(Net::HTTPSuccess)
300
+ raise "Error: Could not fetch gem information from RubyGems for #{gem_name} version #{version}. Status: #{response.code}"
230
301
  end
231
- match = response.match(/<input[^>]*id=["']gemfile_text["'][^>]*value=["']([^"']+)["']/)
302
+
303
+ match = response.body.match(/<input[^>]*id=["']gemfile_text["'][^>]*value=["']([^"']+)["']/)
232
304
 
233
305
  if match
234
306
  CGI.unescapeHTML(match[1])
@@ -268,5 +340,51 @@ module GemSorter
268
340
  versions
269
341
  end
270
342
 
343
+ def fetch_latest_version(gem_name)
344
+ url = URI("https://rubygems.org/api/v1/gems/#{gem_name.strip}.json")
345
+
346
+ begin
347
+ http = Net::HTTP.new(url.host, url.port)
348
+ http.use_ssl = true
349
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
350
+
351
+ request = Net::HTTP::Get.new(url)
352
+ response = http.request(request)
353
+
354
+ unless response.is_a?(Net::HTTPSuccess)
355
+ return nil
356
+ end
357
+
358
+ gem_data = JSON.parse(response.body)
359
+ gem_data['version']
360
+ rescue => e
361
+ nil
362
+ end
363
+ end
364
+
365
+ def extract_current_version(gem_line)
366
+ # Try to extract version from gem line
367
+ # Examples: gem "rails", "~> 7.0" or gem "rails", "7.0.0" or gem "rails", ">= 7.0", "< 8.0"
368
+ # Match first version string after gem name
369
+ version_match = gem_line.match(/gem\s+['"][^'"]+['"]\s*,\s*['"]([^'"]+)['"]/)
370
+ return nil unless version_match
371
+
372
+ version_string = version_match[1]
373
+ # Extract semantic version (x.y.z) from version string, or return the full version string
374
+ semantic_version = version_string.match(/(\d+\.\d+\.\d+)/)
375
+ semantic_version ? semantic_version[1] : version_string
376
+ end
377
+
378
+ def print_version_updates
379
+ return if @version_updates.empty?
380
+
381
+ puts "\nThe following gems were updated:"
382
+ @version_updates.each do |update|
383
+ puts " * #{update[:gem_name]} (#{update[:from_version]} -> #{update[:to_version]})"
384
+ end
385
+ puts "\nRun `bundle install` to install the updated gems."
386
+ puts ""
387
+ end
388
+
271
389
  end
272
390
  end
data/lib/task_config.rb CHANGED
@@ -10,6 +10,7 @@ module GemSorter
10
10
  'update_versions' => false,
11
11
  'use_double_quotes' => false,
12
12
  'remove_versions' => false,
13
+ 'force_update' => false,
13
14
  'gemfile_path' => 'Gemfile',
14
15
  'ignore_gems' => [],
15
16
  'ignore_gem_versions' => [],
@@ -22,6 +23,7 @@ module GemSorter
22
23
  @update_versions = nil
23
24
  @use_double_quotes = nil
24
25
  @remove_versions = nil
26
+ @force_update = nil
25
27
  @ignore_gems = nil
26
28
  @ignore_gem_versions = nil
27
29
  @ignore_gem_comments = nil
@@ -49,6 +51,10 @@ module GemSorter
49
51
  @remove_versions.nil? ? DEFAULT_CONFIG['remove_versions'] : @remove_versions
50
52
  end
51
53
 
54
+ def force_update
55
+ @force_update.nil? ? DEFAULT_CONFIG['force_update'] : @force_update
56
+ end
57
+
52
58
  def ignore_gems
53
59
  @ignore_gems.nil? ? DEFAULT_CONFIG['ignore_gems'] : @ignore_gems
54
60
  end
@@ -82,6 +88,7 @@ module GemSorter
82
88
  @gemfile_path = task_config['gemfile_path'] if task_config.key?('gemfile_path')
83
89
  @use_double_quotes = task_config['use_double_quotes'] if task_config.key?('use_double_quotes')
84
90
  @remove_versions = task_config['remove_versions'] if task_config.key?('remove_versions')
91
+ @force_update = task_config['force_update'] if task_config.key?('force_update')
85
92
  @ignore_gems = task_config['ignore_gems'] if task_config.key?('ignore_gems')
86
93
  @ignore_gem_versions = task_config['ignore_gem_versions'] if task_config.key?('ignore_gem_versions')
87
94
  @ignore_gem_comments = task_config['ignore_gem_comments'] if task_config.key?('ignore_gem_comments')
@@ -96,6 +103,7 @@ module GemSorter
96
103
  @gemfile_path = args[:gemfile_path] unless args[:gemfile_path].nil?
97
104
  @use_double_quotes = parse_boolean(args[:use_double_quotes]) unless args[:use_double_quotes].nil?
98
105
  @remove_versions = parse_boolean(args[:remove_versions]) unless args[:remove_versions].nil?
106
+ @force_update = parse_boolean(args[:force_update]) unless args[:force_update].nil?
99
107
  @ignore_gems = args[:ignore_gems] unless args[:ignore_gems].nil?
100
108
  @ignore_gem_versions = args[:ignore_gem_versions] unless args[:ignore_gem_versions].nil?
101
109
  @ignore_gem_comments = args[:ignore_gem_comments] unless args[:ignore_gem_comments].nil?
@@ -3,8 +3,8 @@ require 'yaml'
3
3
  require 'task_config'
4
4
 
5
5
  namespace :gemfile do
6
- desc 'Sort gems in Gemfile alphabetically. Options: [backup=true|false] [update_comments=true|false] [update_versions=true|false]'
7
- task :sort, [:backup, :update_comments, :update_versions] do |_t, args|
6
+ desc 'Sort gems in Gemfile alphabetically. Options: [backup=true|false] [update_comments=true|false] [update_versions=true|false] [force_update=true|false]'
7
+ task :sort, [:backup, :update_comments, :update_versions, :force_update] do |_t, args|
8
8
  task_config = GemSorter::TaskConfig.new(args)
9
9
 
10
10
  if File.exist?(task_config.gemfile_path)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_sorter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renan Garcia
@@ -70,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
70
  - !ruby/object:Gem::Version
71
71
  version: '0'
72
72
  requirements: []
73
- rubygems_version: 3.6.7
73
+ rubygems_version: 3.6.8
74
74
  specification_version: 4
75
75
  summary: Sort gems in the Gemfile alphabetically
76
76
  test_files: []