rebundler 0.5.0 → 0.6.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: f39f0bba7935159299bb736312beb6b0452d8997bb1f6c5bf574747b08d05af3
4
- data.tar.gz: f8920192f0a6bf48ba31f21f67f8cba06d1a682e00196a89750aed089bb392bb
3
+ metadata.gz: 5b1b0b72d8ab93bb647d7798d0a5baf75b453e279a82488c4e4fb91d27f59889
4
+ data.tar.gz: 74d512496ca8a94348cf3dadd24f866d95d7c84651185a976846fd29b0c0d41f
5
5
  SHA512:
6
- metadata.gz: 8a1144be379a959ed76e26635daf82a9e535dc8e8ac416f2509206c95f9b6673b349f62ed80260f7703e83781cf657d20ef7e0e5ba3b6ef732e7404c6e4cb543
7
- data.tar.gz: fa7d2a0423b94382e778ac048defebfe6301d994d860f1040f1cac509e9f617903234ea2eea1f2c5400f8d020183d14bd370a8617287fe0c1203a474f039d024
6
+ metadata.gz: b821a15b955e764523e0e37bc84c7f44873fa4b9ba73e6223897d862ef96ee90df82412e539968d18ade95460eacfd8956cd03f4f9c0128e48c88bf365059a73
7
+ data.tar.gz: 2d78eec32c5ccb0d4d3378ee024919516e8ecbddcd57ed227f0d63ef9368d15c6e1b3c792c2f30919831167e2f2b1f56ae984ce5ccda81833047e4a7c5b6faab
data/.rubocop.yml CHANGED
@@ -23,6 +23,3 @@ Style/StringConcatenation:
23
23
 
24
24
  Layout/LineLength:
25
25
  Enabled: false
26
-
27
- Lint/ItWithoutArgumentsInBlock:
28
- Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## [0.6.0] - 2026-06-18
2
+
3
+ - Add support for the `install_if` directive. `install_if` blocks are now parsed and
4
+ formatted like other directive blocks (`group`, `path`, etc.).
5
+ - Fixed a crash when a block was empty.
6
+ - Fall back to a gem's `description` when its `summary` is `nil` or blank, so the
7
+ generated trailing comment is still populated.
8
+ - Declare `prism` and `zeitwerk` as runtime dependencies in the gemspec (they were
9
+ previously only in the Gemfile), fixing standalone installs of the gem.
10
+ - Internal refactoring (inlined the summarizer into `GemDeclaration`) and dependency updates.
11
+
12
+ ## [0.5.1] - 2026-03-13
13
+
14
+ - Remove references to running as a bundler plugin. This does not work well, so decided to remove it.
15
+ - Fixed Ruby 3.3 support.
16
+
1
17
  ## [0.5.0] - 2026-03-13
2
18
 
3
19
  - Major internal refactor and clean up.
data/README.md CHANGED
@@ -18,41 +18,15 @@ This is a real life example from my own project. That looks a lot better, doesn'
18
18
 
19
19
  ## Known limitations
20
20
 
21
- - **Probably does not work with all possible Gemfile configurations.** It is designed to work with the most common setups right now. If you encounter an issue, please open an issue on GitHub. I strive to support each sensible configuration.
21
+ - **Probably does not work with all possible Gemfile configurations.** It is designed to work with the most common setups right now. If you encounter an issue, please open an issue on GitHub. I strive to support most sensible configurations.
22
22
 
23
23
  ## Installation
24
24
 
25
- There are two ways to install Rebundler.
25
+ First add `rebundler` to your Gemfile. `bundle add rebundler`. There are two ways to run Rebundler.
26
26
 
27
- ### 1. Automatic mode
27
+ ### 1. Writing mode
28
28
 
29
- > [!NOTE]
30
- > This does not seem to work properly yet, I need to dig into why later.
31
-
32
- Simply add them gem to your Gemfile. the location does not matter as it will be resorted immediately.
33
-
34
- ```ruby
35
- gem "rebundler"
36
- ```
37
-
38
- This installs a Bundler plugin that automatically runs after installing gems. You should see a message in your terminal after running `bundle`:
39
-
40
- ```sh
41
- $ bundle
42
-
43
- ...
44
-
45
- Reordering and annotating Gemfile...
46
- Bundle complete! 10 Gemfile dependencies, 45 gems now installed.
47
- ```
48
-
49
- ### 2. Manual mode
50
-
51
- ```ruby
52
- gem "rebundler", require: false
53
- ```
54
-
55
- This does **not** load the plugin and means you have to run rebundler yourself by running the `rebundle` command:
29
+ If you run `bundle exec rebundle`, rebundler will reorder and annotate your Gemfile.
56
30
 
57
31
  ```sh
58
32
  $ bundle exec rebundle
@@ -60,7 +34,7 @@ Reordering and annotating Gemfile...
60
34
  ✓ Gemfile has been reordered and annotated
61
35
  ```
62
36
 
63
- ### 3. CI mode
37
+ ### 2. CI mode
64
38
 
65
39
  If you run `bundle exec rebundle --ci`, rebundler will run in CI mode, which will compare the output
66
40
  of the current Gemfile to a freshly formatted one.
@@ -69,6 +43,12 @@ If there are differences, rebundler will exit with a non-zero status code.
69
43
 
70
44
  This does not write to the Gemfile.
71
45
 
46
+ ```sh
47
+ ❯ bundle exec rebundle --ci
48
+ Checking if Gemfile is properly formatted...
49
+ ✓ Gemfile is properly formatted
50
+ ```
51
+
72
52
  ### Options
73
53
 
74
54
  ### `--force`
@@ -112,7 +92,7 @@ new_content = parser.format(overwrite_comments: true)
112
92
 
113
93
  ## Development
114
94
 
115
- After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
95
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec minitest` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
116
96
 
117
97
  ## Contributing
118
98
 
@@ -1,15 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rebundler
4
- GemDeclaration = Data.define(:name, :summary, :node) do
4
+ class GemDeclaration
5
+ include Comparable
6
+
7
+ attr_reader :node
8
+
9
+ def initialize(node: nil)
10
+ @node = node
11
+ end
12
+
13
+ def name = @node.arguments.child_nodes[0].content
14
+ def normalized_name = name.tr("-_", "").downcase
15
+
16
+ def summary
17
+ gem = find_loaded_gem || find_external_gem
18
+
19
+ return if gem.nil?
20
+
21
+ if gem.summary.nil? || gem.summary == ""
22
+ gem.description
23
+ else
24
+ gem.summary
25
+ end
26
+ end
27
+
5
28
  def <=>(other)
6
- raise ArgumentError, "comparison of GemDeclaration with #{other.class} failed" unless other.is_a?(GemDeclaration)
29
+ unless other.is_a?(GemDeclaration)
30
+ raise ArgumentError,
31
+ "comparison of GemDeclaration with #{other.class} failed"
32
+ end
7
33
 
8
34
  normalized_name <=> other.normalized_name
9
35
  end
10
36
 
11
- def normalized_name
12
- name.tr("-_", "").downcase
37
+ private
38
+
39
+ def find_loaded_gem
40
+ Gem::Specification.find_by_name(name)
41
+ rescue Gem::MissingSpecError
42
+ nil
43
+ end
44
+
45
+ def find_external_gem
46
+ spec = Gem::SpecFetcher.fetcher.spec_for_dependency(Gem::Dependency.new(name))
47
+
48
+ return if spec.nil? || spec.first.empty?
49
+
50
+ spec.first.first.first
51
+ rescue SocketError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Timeout::Error => e
52
+ raise Rebundler::Error, "Network error while fetching gem info for '#{name}': #{e.message}"
13
53
  end
14
54
  end
15
55
  end
@@ -16,7 +16,10 @@ module Rebundler
16
16
  end
17
17
 
18
18
  def <=>(other)
19
- raise ArgumentError, "comparison of GemSet with #{other.class} failed" unless other.is_a?(GemSet)
19
+ unless other.is_a?(GemSet)
20
+ raise ArgumentError,
21
+ "comparison of GemSet with #{other.class} failed"
22
+ end
20
23
 
21
24
  # Default first, then sort by name
22
25
  [default ? 0 : 1, name.to_s] <=> [other.default ? 0 : 1, other.name.to_s]
@@ -41,7 +41,7 @@ module Rebundler
41
41
 
42
42
  def self.find_spacing_character(node)
43
43
  # Assume it's formatted with tabs if ANY line starts with a tab.
44
- any_line_starts_with_tab = node.location.slice.split("\n").any? { it.start_with?("\t") }
44
+ any_line_starts_with_tab = node.location.slice.split("\n").any? { |line| line.start_with?("\t") }
45
45
 
46
46
  any_line_starts_with_tab ? "\t" : " "
47
47
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rebundler
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -11,13 +11,18 @@ module Rebundler
11
11
  def visit_call_node(node)
12
12
  case node.name
13
13
  when :plugin
14
- @current_set.plugins << parse_gem_with_comment(node)
14
+ @current_set.plugins << GemDeclaration.new(node:)
15
15
  when :gem
16
- @current_set.gems << parse_gem_with_comment(node)
16
+ @current_set.gems << GemDeclaration.new(node:)
17
17
  when :git_source
18
+ # :git_source is a special case where it's a directive with a block that
19
+ # we don't want to add it as a set, which would happen if it's parsed in
20
+ # the condition for DIRECTIVE_AND_BLOCK_NODES below.
18
21
  @parser.directives << node
19
22
  when *DIRECTIVE_AND_BLOCK_NODES
20
23
  if node.block
24
+ return unless node.block.body
25
+
21
26
  # Use the full block declaration (e.g., "group :development do") as the unique name
22
27
  name = node.location.slice.lines.first.strip
23
28
 
@@ -42,12 +47,5 @@ module Rebundler
42
47
  @parser.gem_sets << set
43
48
  end
44
49
  end
45
-
46
- def parse_gem_with_comment(node)
47
- name = node.arguments.child_nodes[0].content
48
- summary = Summarizer.summarize(name)
49
-
50
- GemDeclaration.new(name:, summary:, node:)
51
- end
52
50
  end
53
51
  end
data/lib/rebundler.rb CHANGED
@@ -2,12 +2,11 @@
2
2
 
3
3
  require "zeitwerk"
4
4
  loader = Zeitwerk::Loader.for_gem
5
- loader.ignore("#{__dir__}/bundler")
6
5
  loader.setup
7
6
 
8
7
  module Rebundler
9
8
  class Error < StandardError; end
10
9
 
11
10
  SORTABLE_NODES = %i[plugin gem].freeze
12
- DIRECTIVE_AND_BLOCK_NODES = %i[gemspec git group path platforms ruby source].freeze
11
+ DIRECTIVE_AND_BLOCK_NODES = %i[gemspec git group path platforms ruby source install_if].freeze
13
12
  end
metadata CHANGED
@@ -1,14 +1,42 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rebundler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dennis Paagman
8
8
  bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: prism
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.9'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.9'
26
+ - !ruby/object:Gem::Dependency
27
+ name: zeitwerk
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.8'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.8'
12
40
  email:
13
41
  - dennis@paagman.dev
14
42
  executables:
@@ -22,7 +50,6 @@ files:
22
50
  - README.md
23
51
  - Rakefile
24
52
  - exe/rebundle
25
- - lib/bundler/rebundle.rb
26
53
  - lib/rebundler.rb
27
54
  - lib/rebundler/Rakefile
28
55
  - lib/rebundler/formatter.rb
@@ -30,11 +57,9 @@ files:
30
57
  - lib/rebundler/gem_set.rb
31
58
  - lib/rebundler/parser.rb
32
59
  - lib/rebundler/serializer.rb
33
- - lib/rebundler/summarizer.rb
34
60
  - lib/rebundler/version.rb
35
61
  - lib/rebundler/visitor.rb
36
62
  - mise.toml
37
- - plugins.rb
38
63
  homepage: https://github.com/dennispaagman/rebundler
39
64
  licenses:
40
65
  - MIT
@@ -57,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
82
  - !ruby/object:Gem::Version
58
83
  version: '0'
59
84
  requirements: []
60
- rubygems_version: 4.0.3
85
+ rubygems_version: 4.0.10
61
86
  specification_version: 4
62
87
  summary: Rebundler makes your Gemfile look good.
63
88
  test_files: []
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler"
4
- require "tempfile"
5
-
6
- module Bundler
7
- class Rebundle < Bundler::Plugin::API
8
- command "rebundle"
9
-
10
- hook "after-install-all" do
11
- parse_and_write_gemfile!
12
- end
13
-
14
- def exec(command, args)
15
- send command, args
16
- end
17
-
18
- def self.parse_and_write_gemfile!
19
- Bundler.ui.info "Reordering and annotating Gemfile..."
20
-
21
- file = Bundler.root.to_s + "/Gemfile"
22
-
23
- begin
24
- # Load all gems in the Gemfile so we can use them to grab the summary instead of
25
- # having to hit rubygems.org
26
- Bundler.setup
27
-
28
- parser = ::Rebundler::Parser.from_file(file)
29
- content = parser.format
30
-
31
- # Use atomic write to prevent corruption if write fails
32
- Tempfile.create("Gemfile", Bundler.root.to_s) do |tmp|
33
- tmp.write(content)
34
- tmp.flush
35
- tmp.close
36
- FileUtils.mv(tmp.path, file)
37
- end
38
- rescue StandardError => e
39
- Bundler.ui.error "Error parsing Gemfile: #{e.message}"
40
- raise Bundler::PluginError, e
41
- end
42
- end
43
- end
44
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "timeout"
4
-
5
- module Rebundler
6
- class Summarizer
7
- class << self
8
- def summarize(name)
9
- find_loaded_gem_summary(name) || find_external_gem_summary(name)
10
- end
11
-
12
- private
13
-
14
- def find_loaded_gem_summary(name)
15
- gem = Gem::Specification.find_by_name(name)
16
-
17
- gem.summary
18
- rescue Gem::MissingSpecError
19
- nil
20
- end
21
-
22
- def find_external_gem_summary(name)
23
- spec = Gem::SpecFetcher.fetcher.spec_for_dependency Gem::Dependency.new(name)
24
-
25
- return if spec.nil? || spec.first.empty?
26
-
27
- spec.first.first.first.summary
28
- rescue SocketError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Timeout::Error => e
29
- raise Rebundler::Error, "Network error while fetching gem info for '#{name}': #{e.message}"
30
- end
31
- end
32
- end
33
- end
data/plugins.rb DELETED
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "lib/rebundler"