gem_sorter 0.1.1 → 0.3.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: d91ddab7a3117bdca0c33158d6b6cb836e1eb75126ef8e101335bc0ddb55daa4
4
- data.tar.gz: 0b059c69f84a26e5f55b603777e73a1941b3be1e0bc64f7b247b213ce27fed04
3
+ metadata.gz: 91f751af186dcb78cb90b9acbf22c086e895dc6b74ad6b202783fe3f6d799a3d
4
+ data.tar.gz: dc18ef774b7be132570b526e510c95022b0da490b45fd4483d6ec6ae39a0b1aa
5
5
  SHA512:
6
- metadata.gz: 70d59f3a8cd42b31378ba99433db9e638aca693ad0c4cf83143f12aa4e381bdd6e34c1e6067a56633d0fc20282993bc35e4c0b3c1c36ffeaf8d83514b2927b26
7
- data.tar.gz: f64d158253309097b7b5cf9b4421414ea4d69635f27e8e071a67ddc8b084a7f751f47f0973cbbec6e1b0ff9fb71e90f6bb2c3dad8461cb61faa649b189ab1d64
6
+ metadata.gz: 3c527d936237f42547330dc39c740aaca01788a20a3c6e73024ebcddd11dbbce80d74bdeab5d7c39391d7beca6b23264a20fd25e79a1f44c58bb588164ae7238
7
+ data.tar.gz: 9a640f1ee3d35cd1aa019cea2b9e0eb7898c3d39d0635334660f527073faeea0d5cf860ce5dfc7c90478d8c5f2361b61dbb083621805f95ae153a7900ef85d26
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <img src="https://i.imgur.com/WHOyL9W.png" width="150" alt="GemSorter Logo">
3
+ </p>
4
+
1
5
  # GemSorter
2
6
 
3
7
  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.
@@ -7,6 +11,7 @@ GemSorter is a simple gem to sort the contents of your Gemfile alphabetically wh
7
11
  * Preserves comments and their association with gems.
8
12
  * Maintains group structure in the Gemfile.
9
13
  * Optionally creates a backup of the original Gemfile.
14
+ * Update the comments of the gems based on their descriptions.
10
15
 
11
16
  ## Installation
12
17
  Add the gem to your project's `Gemfile`:
@@ -24,15 +29,40 @@ rake gemfile:sort
24
29
 
25
30
  ### Options
26
31
  * `backup`: Pass `true` to create a backup of your Gemfile as `Gemfile.old` before sorting.
32
+ * `update_comments`: Pass `true` to update the comments of the gems based on their descriptions.
33
+ * `update_versions`: Pass `true` to update the versions of the gems based on the lockfile.
27
34
 
28
35
  Example:
29
36
 
30
37
  ```bash
31
- rake gemfile:sort[true]
38
+ rake gemfile:sort[true,true,true]
32
39
  ```
40
+ This will sort your Gemfile, create a backup, and update comments and versions.
33
41
 
34
- This will sort your Gemfile and create a backup.
42
+ ### Options File
43
+ Create a file in the root of your project called `gem_sorter.yml`
44
+ * `backup`: Pass `true` to create a backup of your Gemfile as `Gemfile.old` before sorting.
45
+ * `update_comments`: Pass `true` to update the comments of the gems based on their descriptions.
46
+ * `update_versions`: Pass `true` to update the versions of the gems based on the lockfile.
47
+ * `ignore_gems`: Pass an array of GEMs you want to ignore versions and comments
48
+ * `ignore_gem_versions`: Pass an array of GEMs you want to ignore versions
49
+ * `ignore_gem_comments`: Pass an array of GEMs you want to ignore comments
35
50
 
51
+ Example:
52
+
53
+ ```yaml
54
+ backup: true
55
+ update_comments: true
56
+ update_versions: false
57
+ ignore_gems:
58
+ - byebug
59
+ - discard
60
+ ignore_gem_versions:
61
+ - rspec
62
+ ignore_gem_comments:
63
+ - otpor
64
+ ```
65
+ This will sort your Gemfile, create a backup, and update comments and versions.
36
66
  ## Example
37
67
  ### Input Gemfile
38
68
  ```ruby
@@ -40,12 +70,11 @@ source "https://rubygems.org"
40
70
 
41
71
  # Framework
42
72
  gem "rails"
43
- # Web server
44
- gem "puma"
73
+ gem "puma", "~> 5.3"
45
74
 
46
75
  group :development do
47
- gem "pry"
48
76
  gem "dotenv-rails"
77
+ gem "pry"
49
78
  end
50
79
  ```
51
80
 
@@ -53,13 +82,15 @@ end
53
82
  ```ruby
54
83
  source "https://rubygems.org"
55
84
 
56
- # Web server
57
- gem "puma"
58
- # Framework
59
- gem "rails"
85
+ # A Ruby/Rack web server built for parallelism.
86
+ gem "puma", "~> 5.3"
87
+ # Full-stack web application framework.
88
+ gem "rails", "~> 8.0", ">= 8.0.1"
60
89
 
61
90
  group :development do
62
- gem "dotenv-rails"
91
+ # Autoload dotenv in Rails.
92
+ gem 'dotenv-rails', '~> 3.1', '>= 3.1.7'
93
+ # A runtime developer console and IRB alternative with powerful introspection capabilities.
63
94
  gem "pry"
64
95
  end
65
96
  ```
@@ -1,3 +1,3 @@
1
1
  module GemSorter
2
- VERSION = '0.1.1'
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/gem_sorter.rb CHANGED
@@ -1,12 +1,16 @@
1
1
  # lib/gem_sorter.rb
2
+ require 'net/http'
3
+ require 'cgi'
2
4
 
3
5
  load File.expand_path('tasks/gem_sorter.rake', __dir__) if defined?(Rake)
4
6
 
5
7
  module GemSorter
6
8
  class Sorter
7
- def initialize(filepath)
8
- @filepath = filepath
9
- @content = File.read(filepath)
9
+ def initialize(task_config)
10
+ @config = task_config
11
+ @filepath = task_config.gemfile_path
12
+ @content = File.read(task_config.gemfile_path)
13
+ @versions = nil
10
14
  end
11
15
 
12
16
  def sort
@@ -16,6 +20,9 @@ module GemSorter
16
20
 
17
21
  source_line, gems = process_main_section(main_section)
18
22
 
23
+ update_gem_summaries(gems) if @config.update_comments
24
+ update_version_text(gems) if @config.update_versions
25
+
19
26
  sorted_gems = sort_gem_blocks(gems)
20
27
 
21
28
  result = []
@@ -26,6 +33,8 @@ module GemSorter
26
33
 
27
34
  group_sections.each do |section|
28
35
  group_gems = process_group_section(section)
36
+ update_gem_summaries(group_gems) if @config.update_comments
37
+ update_version_text(group_gems) if @config.update_versions
29
38
  result << "group#{section.split("\n").first}"
30
39
  result.concat(sort_gem_blocks(group_gems).map { |line| " #{line}" })
31
40
  result << 'end'
@@ -37,6 +46,44 @@ module GemSorter
37
46
 
38
47
  private
39
48
 
49
+ def update_version_text(gems)
50
+ @versions ||= fetch_versions_from_lockfile("#{@filepath}.lock")
51
+ gems.each do |gem_block|
52
+ gem_name = gem_block[:gem_line].match(/gem\s*"([^"]+)"/)[1]
53
+ next if @config.ignore_gems.include?(gem_name) || @config.ignore_gem_versions.include?(gem_name)
54
+
55
+ version = @versions[gem_name]
56
+ extra_params = extract_params(gem_block[:gem_line])
57
+ base = version ? "#{fetch_gemfile_text(gem_name, version, gem_block[:gem_line])}" : gem_block[:gem_line]
58
+ return base if base == gem_block[:gem_line]
59
+
60
+ gem_block[:gem_line] = [base, extra_params].select { |value| !value.nil? && !value.empty? }.join(',')
61
+ end
62
+ end
63
+
64
+ def extract_params(gem_line)
65
+ return nil unless gem_line =~ /gem\s*"([^"]+)"/
66
+
67
+ if gem_line =~ /gem\s*"[^"]*"\s*,\s*(.*)/
68
+ additional_params = $1.strip
69
+ return nil if additional_params.empty?
70
+ params_with_colon = additional_params.scan(/(\w+:\s*[^,]+)/).flatten
71
+ return params_with_colon.join(', ') unless params_with_colon.empty?
72
+ end
73
+ nil
74
+ end
75
+
76
+ def update_gem_summaries(gems)
77
+ gems.each do |gem_block|
78
+ gem_name = gem_block[:gem_line].match(/gem\s*"([^"]+)"/)[1]
79
+ next if @config.ignore_gems.include?(gem_name) || @config.ignore_gem_comments.include?(gem_name)
80
+
81
+ if summary = get_summary(gem_name, false)
82
+ gem_block[:comments] = ["# #{summary}"]
83
+ end
84
+ end
85
+ end
86
+
40
87
  def process_main_section(section)
41
88
  lines = section.split("\n").map(&:strip).reject(&:empty?)
42
89
  source_line = lines.shift
@@ -83,7 +130,12 @@ module GemSorter
83
130
 
84
131
  def sort_gem_blocks(gems)
85
132
  sorted = gems.sort_by do |gem_block|
86
- gem_block[:gem_line].match(/gem\s*"([^"]+)"/)[1].downcase
133
+ match_data = gem_block[:gem_line].match(/gem\s*['"]([^'"]+)['"]/)
134
+ if match_data
135
+ match_data[1].downcase
136
+ else
137
+ ''
138
+ end
87
139
  end
88
140
 
89
141
  result = []
@@ -94,5 +146,69 @@ module GemSorter
94
146
 
95
147
  result
96
148
  end
149
+
150
+ def get_summary(gem_name, remote)
151
+ source = remote ? "-r" : "-l"
152
+ output = `gem list -d #{source} -e #{gem_name}`
153
+ if output.include?(gem_name)
154
+ summary = output.split("\n").last.strip
155
+ summary
156
+ else
157
+ return get_summary(gem_name, true) unless remote
158
+ nil
159
+ end
160
+ rescue StandardError => e
161
+ nil
162
+ end
163
+
164
+ def fetch_gemfile_text(gem_name, version, original)
165
+ base_url = "https://rubygems.org/gems/#{gem_name.strip}"
166
+ url = URI(version ? "#{base_url}/versions/#{version.strip}" : base_url)
167
+
168
+ begin
169
+ response = Net::HTTP.get(url)
170
+ unless response
171
+ raise "Error: Could not fetch gem information from RubyGems for #{gem_name} version #{version}."
172
+ end
173
+ match = response.match(/<input[^>]*id=["']gemfile_text["'][^>]*value=["']([^"']+)["']/)
174
+
175
+ if match
176
+ CGI.unescapeHTML(match[1])
177
+ else
178
+ raise "Error: Could not extract Gemfile text for #{gem_name} version #{version}."
179
+ end
180
+
181
+ rescue => e
182
+ puts e.message
183
+
184
+ original
185
+ end
186
+ end
187
+
188
+ def fetch_versions_from_lockfile(lockfile_path)
189
+ return {} unless File.exist?(lockfile_path)
190
+
191
+ versions = {}
192
+ inside_specs = false
193
+
194
+ File.readlines(lockfile_path).each do |line|
195
+ line.strip!
196
+
197
+ if line == "specs:"
198
+ inside_specs = true
199
+ next
200
+ end
201
+
202
+ inside_specs = false if inside_specs && line.empty?
203
+
204
+ if inside_specs && line =~ /^([^\s]+)\s\(([^)]+)\)$/
205
+ gem_name, gem_version = $1, $2
206
+ gem_version = gem_version.match(/(\d+\.\d+\.\d+)/)[0] if gem_version =~ /(\d+\.\d+\.\d+)/
207
+ versions[gem_name] = gem_version
208
+ end
209
+ end
210
+ versions
211
+ end
212
+
97
213
  end
98
- end
214
+ end
@@ -0,0 +1,94 @@
1
+ require 'yaml'
2
+
3
+ class GemSorter::TaskConfig
4
+ attr_reader :backup, :update_comments, :update_versions, :ignore_gems, :gemfile_path
5
+
6
+ DEFAULT_CONFIG = {
7
+ 'backup' => false,
8
+ 'update_comments' => false,
9
+ 'update_versions' => false,
10
+ 'gemfile_path' => 'Gemfile',
11
+ 'ignore_gems' => [],
12
+ 'ignore_gem_versions' => [],
13
+ 'ignore_gem_comments' => [],
14
+ }.freeze
15
+
16
+ def initialize(args = nil)
17
+ @backup = nil
18
+ @update_comments = nil
19
+ @update_versions = nil
20
+ @ignore_gems = nil
21
+ @ignore_gem_versions = nil
22
+ @ignore_gem_comments = nil
23
+ @gemfile_path = nil
24
+ load_config(args)
25
+ end
26
+
27
+ def backup
28
+ @backup.nil? ? DEFAULT_CONFIG['backup'] : @backup
29
+ end
30
+
31
+ def update_comments
32
+ @update_comments.nil? ? DEFAULT_CONFIG['update_comments'] : @update_comments
33
+ end
34
+
35
+ def update_versions
36
+ @update_versions.nil? ? DEFAULT_CONFIG['update_versions'] : @update_versions
37
+ end
38
+
39
+ def ignore_gems
40
+ @ignore_gems.nil? ? DEFAULT_CONFIG['ignore_gems'] : @ignore_gems
41
+ end
42
+
43
+ def ignore_gem_versions
44
+ @ignore_gem_versions.nil? ? DEFAULT_CONFIG['ignore_gem_versions'] : @ignore_gem_versions
45
+ end
46
+
47
+ def ignore_gem_comments
48
+ @ignore_gem_comments.nil? ? DEFAULT_CONFIG['ignore_gem_comments'] : @ignore_gem_comments
49
+ end
50
+
51
+ def gemfile_path
52
+ @gemfile_path.nil? ? DEFAULT_CONFIG['gemfile_path'] : @gemfile_path
53
+ end
54
+
55
+ private
56
+
57
+ def load_config(args)
58
+ load_config_from_file
59
+ load_config_from_args(args)
60
+ end
61
+
62
+ def load_config_from_file
63
+ if File.exist?(gem_sorter_config_file_path)
64
+ task_config = YAML.load_file(gem_sorter_config_file_path)
65
+ @backup = task_config['backup'] if task_config.key?('backup')
66
+ @update_comments = task_config['update_comments'] if task_config.key?('update_comments')
67
+ @update_versions = task_config['update_versions'] if task_config.key?('update_versions')
68
+ @gemfile_path = task_config['gemfile_path'] if task_config.key?('gemfile_path')
69
+ @ignore_gems = task_config['ignore_gems'] if task_config.key?('ignore_gems')
70
+ @ignore_gem_versions = task_config['ignore_gem_versions'] if task_config.key?('ignore_gem_versions')
71
+ @ignore_gem_comments = task_config['ignore_gem_comments'] if task_config.key?('ignore_gem_comments')
72
+ end
73
+ end
74
+
75
+ def load_config_from_args(args)
76
+ return unless args
77
+
78
+ @backup = parse_boolean(args[:backup]) unless args[:backup].nil?
79
+ @update_comments = parse_boolean(args[:update_comments]) unless args[:update_comments].nil?
80
+ @update_versions = parse_boolean(args[:update_versions]) unless args[:update_versions].nil?
81
+ @gemfile_path = args[:gemfile_path] unless args[:gemfile_path].nil?
82
+ @ignore_gems = args[:ignore_gems] unless args[:ignore_gems].nil?
83
+ @ignore_gem_versions = args[:ignore_gem_versions] unless args[:ignore_gem_versions].nil?
84
+ @ignore_gem_comments = args[:ignore_gem_comments] unless args[:ignore_gem_comments].nil?
85
+ end
86
+
87
+ def parse_boolean(value)
88
+ value.to_s.downcase == 'true'
89
+ end
90
+
91
+ def gem_sorter_config_file_path
92
+ 'gem_sorter.yml'
93
+ end
94
+ end
@@ -1,25 +1,26 @@
1
1
  require 'gem_sorter'
2
+ require 'yaml'
3
+ require 'task_config'
2
4
 
3
5
  namespace :gemfile do
4
- desc 'Sort gems in Gemfile alphabetically'
5
- task :sort, [:backup] do |_t, args|
6
- args.with_defaults(backup: 'false')
7
- gemfile_path = 'Gemfile'
8
-
9
- if File.exist?(gemfile_path)
10
- if args.backup.downcase == 'true'
11
- FileUtils.cp(gemfile_path, "#{gemfile_path}.old")
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|
8
+ task_config = GemSorter::TaskConfig.new(args)
9
+
10
+ if File.exist?(task_config.gemfile_path)
11
+ if task_config.backup
12
+ FileUtils.cp(task_config.gemfile_path, "#{task_config.gemfile_path}.old")
12
13
  puts 'Original Gemfile backed up as Gemfile.old'
13
14
  end
14
15
 
15
- sorter = ::GemSorter::Sorter.new(gemfile_path)
16
+ sorter = ::GemSorter::Sorter.new(task_config)
16
17
  sorted_content = sorter.sort
17
18
 
18
- File.write(gemfile_path, sorted_content)
19
+ File.write(task_config.gemfile_path, sorted_content)
19
20
 
20
21
  puts 'Gemfile sorted successfully!'
21
22
  else
22
23
  puts 'Error: Gemfile not found in current directory'
23
24
  end
24
25
  end
25
- end
26
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_sorter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renan Garcia
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-02 00:00:00.000000000 Z
10
+ date: 2025-01-06 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rake
@@ -48,6 +48,7 @@ files:
48
48
  - README.md
49
49
  - lib/gem_sorter.rb
50
50
  - lib/gem_sorter/version.rb
51
+ - lib/task_config.rb
51
52
  - lib/tasks/gem_sorter.rake
52
53
  homepage: https://github.com/renan-garcia/gem_sorter
53
54
  licenses: