gem_sorter 0.5.2 → 0.5.6

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: 3a14433575929ce85e046a16391ef12ca535aaf889e270311831d6afc84d39c7
4
- data.tar.gz: efffe4275cca1eb81d9f824e858945748070137c57d0c6872ec66b985aca7125
3
+ metadata.gz: c19d0f251f29c0467ca815ced49892bd916c2a9090057f5b0303cb5a5c662b95
4
+ data.tar.gz: 90efd7fd7a67541856a5889cdc3fded9955e8e3006ad4639e3a631c2cf33ddaf
5
5
  SHA512:
6
- metadata.gz: dcc7cc1833d9c17b4b04e4a0442fa8e09630a48cce2eae2bf316726f4f267ce77e0d23e87b60783d776c1da7433ebb5ca06f0eaf72f8efb33c520628f74af27d
7
- data.tar.gz: 3c5b1e7ae3724f8b3143daac366df453c9196a92b5123e6972a9ea2713d32a9f46a161625a5c817f9a074896a7d2c91a90cb78fb73b370db5e4ed8f43d47f496
6
+ metadata.gz: 30c2df4815a3232b8c476dfddc74edad9f90537c0d36ddcbb04143693e5c4033cd3ae445b0cb36c5a9edffb692a33fe51adb9f17a512e1cae40edc332198873c
7
+ data.tar.gz: bea536e4ea2bf06e0ebdfebad84ee7bfe4d256cd94af23f5cc52559aeebb811d8bbe0a033fde91d9c4efdf6a111f0cc159ef163f7dd5ce8de505e6545a263399
data/README.md CHANGED
@@ -36,7 +36,20 @@ Once installed, you can use the provided Rake task to sort your Gemfile:
36
36
  rake gemfile:sort
37
37
  ```
38
38
 
39
- You can also run the gem_sorter globally, without needing to add it to your Gemfile:
39
+ ### Inside a Rails application
40
+ When you add `gem_sorter` to a Rails app's `Gemfile`, the `gemfile:sort` task is
41
+ registered automatically through a Railtie, using Rails' own task-loading
42
+ pipeline. This means the task is reliably available under both `bin/rails` and
43
+ `bin/rake`, and the gem no longer defines tasks during application boot:
44
+
45
+ ```bash
46
+ bin/rails gemfile:sort
47
+ # or
48
+ bin/rake gemfile:sort
49
+ ```
50
+
51
+ ### Standalone (global)
52
+ You can also run gem_sorter globally, without adding it to your Gemfile:
40
53
 
41
54
  ```bash
42
55
  rake -r gem_sorter gemfile:sort
@@ -46,6 +59,7 @@ rake -r gem_sorter gemfile:sort
46
59
  * `backup`: Pass `true` to create a backup of your Gemfile as `Gemfile.old` before sorting.
47
60
  * `update_comments`: Pass `true` to update the comments of the gems based on their descriptions.
48
61
  * `update_versions`: Pass `true` to update the versions of the gems based on the lockfile.
62
+ * `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`.
49
63
  * `use_double_quotes`: Pass `true` to convert single quotes to double quotes in gem declarations.
50
64
  * `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.
51
65
 
@@ -61,6 +75,7 @@ Create a file in the root of your project called `gem_sorter.yml`
61
75
  * `backup`: Pass `true` to create a backup of your Gemfile as `Gemfile.old` before sorting.
62
76
  * `update_comments`: Pass `true` to update the comments of the gems based on their descriptions.
63
77
  * `update_versions`: Pass `true` to update the versions of the gems based on the lockfile.
78
+ * `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`.
64
79
  * `use_double_quotes`: Pass `true` to convert single quotes to double quotes in gem declarations.
65
80
  * `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.
66
81
  * `ignore_gems`: Pass an array of GEMs you want to ignore versions and comments
@@ -73,6 +88,7 @@ Example:
73
88
  backup: true
74
89
  update_comments: true
75
90
  update_versions: false
91
+ force_update: false
76
92
  use_double_quotes: true
77
93
  remove_versions: true
78
94
  ignore_gems:
@@ -0,0 +1,13 @@
1
+ require 'rails/railtie'
2
+
3
+ module GemSorter
4
+ # Registers the gem's rake tasks through the Rails task-loading pipeline.
5
+ # Using a Railtie ensures the tasks are available reliably inside a Rails
6
+ # application (under both `bin/rails` and `bin/rake`) without defining tasks
7
+ # during `Bundler.require`, which is order-dependent and could break boot.
8
+ class Railtie < Rails::Railtie
9
+ rake_tasks do
10
+ load File.expand_path('../tasks/gem_sorter.rake', __dir__)
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module GemSorter
2
- VERSION = '0.5.2'
2
+ VERSION = '0.5.6'
3
3
  end
data/lib/gem_sorter.rb CHANGED
@@ -2,8 +2,16 @@
2
2
  require 'net/http'
3
3
  require 'cgi'
4
4
  require 'openssl'
5
+ require 'json'
5
6
 
6
- load File.expand_path('tasks/gem_sorter.rake', __dir__) if defined?(Rake)
7
+ # Inside a Rails app, let the Railtie register the rake tasks through Rails'
8
+ # own task-loading pipeline (reliable and not tied to require order). When used
9
+ # standalone (e.g. `rake -r gem_sorter gemfile:sort`), load the tasks directly.
10
+ if defined?(Rails::Railtie)
11
+ require 'gem_sorter/railtie'
12
+ elsif defined?(Rake)
13
+ load File.expand_path('tasks/gem_sorter.rake', __dir__)
14
+ end
7
15
 
8
16
  module GemSorter
9
17
  class Sorter
@@ -12,6 +20,7 @@ module GemSorter
12
20
  @filepath = task_config.gemfile_path
13
21
  @content = File.read(task_config.gemfile_path)
14
22
  @versions = nil
23
+ @version_updates = []
15
24
  end
16
25
 
17
26
  def sort
@@ -24,7 +33,11 @@ module GemSorter
24
33
  source_line, gems, _ = process_main_section(main_section)
25
34
 
26
35
  update_gem_summaries(gems) if @config.update_comments
27
- update_version_text(gems) if @config.update_versions
36
+ if @config.force_update
37
+ force_update_versions(gems)
38
+ elsif @config.update_versions
39
+ update_version_text(gems)
40
+ end
28
41
  remove_versions(gems) if @config.remove_versions
29
42
  sorted_gems = sort_gem_blocks(gems)
30
43
 
@@ -41,7 +54,11 @@ module GemSorter
41
54
  group_sections.each do |section|
42
55
  group_gems = process_group_section(section)
43
56
  update_gem_summaries(group_gems) if @config.update_comments
44
- update_version_text(group_gems) if @config.update_versions
57
+ if @config.force_update
58
+ force_update_versions(group_gems)
59
+ elsif @config.update_versions
60
+ update_version_text(group_gems)
61
+ end
45
62
  remove_versions(group_gems) if @config.remove_versions
46
63
  result << "group#{section.split("\n").first}"
47
64
  result.concat(sort_gem_blocks(group_gems).map { |line| " #{line}" })
@@ -51,6 +68,8 @@ module GemSorter
51
68
 
52
69
  result = transform_to_double_quotes(result) if @config.use_double_quotes
53
70
 
71
+ print_version_updates if @config.force_update && !@version_updates.empty?
72
+
54
73
  result.join("\n")
55
74
  end
56
75
 
@@ -113,6 +132,44 @@ module GemSorter
113
132
  end
114
133
  end
115
134
 
135
+ def force_update_versions(gems)
136
+ @versions ||= fetch_versions_from_lockfile("#{@filepath}.lock")
137
+
138
+ gems.each do |gem_block|
139
+ gem_name = extract_gem_name(gem_block[:gem_line])
140
+ next if @config.ignore_gems.include?(gem_name) || @config.ignore_gem_versions.include?(gem_name)
141
+
142
+ original_line = gem_block[:gem_line]
143
+ latest_version = fetch_latest_version(gem_name)
144
+
145
+ next unless latest_version
146
+
147
+ extra_params = extract_params(original_line)
148
+ new_gemfile_text = fetch_gemfile_text(gem_name, latest_version, original_line)
149
+
150
+ next unless new_gemfile_text && new_gemfile_text != original_line
151
+
152
+ gem_block[:gem_line] = [new_gemfile_text.strip, extra_params].select { |value| !value.nil? && !value.empty? }.join(',')
153
+
154
+ next unless version_changed?(original_line, new_gemfile_text)
155
+
156
+ @version_updates << {
157
+ gem_name: gem_name,
158
+ from_version: extract_current_version(original_line) || 'no version specified',
159
+ to_version: latest_version
160
+ }
161
+ end
162
+ end
163
+
164
+ # Detects whether a gem line actually changed version between what is declared
165
+ # in the Gemfile and what RubyGems returns. The comparison is based purely on
166
+ # the version constraint, ignoring cosmetic differences (e.g. quote style), so
167
+ # re-running the task does not report the same update again. The lockfile is
168
+ # intentionally not consulted here: it stays stale until `bundle install` runs.
169
+ def version_changed?(original_line, new_gemfile_text)
170
+ extract_current_version(original_line) != extract_current_version(new_gemfile_text)
171
+ end
172
+
116
173
  def remove_versions(gems)
117
174
  gems.each do |gem_block|
118
175
  gem_name = extract_gem_name(gem_block[:gem_line])
@@ -291,5 +348,51 @@ module GemSorter
291
348
  versions
292
349
  end
293
350
 
351
+ def fetch_latest_version(gem_name)
352
+ url = URI("https://rubygems.org/api/v1/gems/#{gem_name.strip}.json")
353
+
354
+ begin
355
+ http = Net::HTTP.new(url.host, url.port)
356
+ http.use_ssl = true
357
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
358
+
359
+ request = Net::HTTP::Get.new(url)
360
+ response = http.request(request)
361
+
362
+ unless response.is_a?(Net::HTTPSuccess)
363
+ return nil
364
+ end
365
+
366
+ gem_data = JSON.parse(response.body)
367
+ gem_data['version']
368
+ rescue => e
369
+ nil
370
+ end
371
+ end
372
+
373
+ def extract_current_version(gem_line)
374
+ # Try to extract version from gem line
375
+ # Examples: gem "rails", "~> 7.0" or gem "rails", "7.0.0" or gem "rails", ">= 7.0", "< 8.0"
376
+ # Match first version string after gem name
377
+ version_match = gem_line.match(/gem\s+['"][^'"]+['"]\s*,\s*['"]([^'"]+)['"]/)
378
+ return nil unless version_match
379
+
380
+ version_string = version_match[1]
381
+ # Extract semantic version (x.y.z) from version string, or return the full version string
382
+ semantic_version = version_string.match(/(\d+\.\d+\.\d+)/)
383
+ semantic_version ? semantic_version[1] : version_string
384
+ end
385
+
386
+ def print_version_updates
387
+ return if @version_updates.empty?
388
+
389
+ puts "\nThe following gems were updated:"
390
+ @version_updates.each do |update|
391
+ puts " * #{update[:gem_name]} (#{update[:from_version]} -> #{update[:to_version]})"
392
+ end
393
+ puts "\nRun `bundle install` to install the updated gems."
394
+ puts ""
395
+ end
396
+
294
397
  end
295
398
  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.2
4
+ version: 0.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renan Garcia
@@ -47,6 +47,7 @@ extra_rdoc_files: []
47
47
  files:
48
48
  - README.md
49
49
  - lib/gem_sorter.rb
50
+ - lib/gem_sorter/railtie.rb
50
51
  - lib/gem_sorter/version.rb
51
52
  - lib/task_config.rb
52
53
  - lib/tasks/gem_sorter.rake
@@ -70,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
71
  - !ruby/object:Gem::Version
71
72
  version: '0'
72
73
  requirements: []
73
- rubygems_version: 3.6.8
74
+ rubygems_version: 3.6.9
74
75
  specification_version: 4
75
76
  summary: Sort gems in the Gemfile alphabetically
76
77
  test_files: []