rebundler 0.5.1 → 0.6.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 +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +43 -6
- data/lib/rebundler/gem_declaration.rb +46 -4
- data/lib/rebundler/gem_set.rb +4 -1
- data/lib/rebundler/version.rb +1 -1
- data/lib/rebundler/visitor.rb +7 -9
- data/lib/rebundler.rb +1 -1
- metadata +31 -4
- data/lib/rebundler/summarizer.rb +0 -33
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bd90571682828dd3a39d81faadcca42fb3003f2cdac7709b060b375dd43c44db
|
|
4
|
+
data.tar.gz: d3d922f26bc42111b9f333d714f8c89776f6a7d4c2541da245acbc29d803e4e8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ff8197f9fc485081470bc387a897a702e05e34b2f5a15d910f6227df07f9cf917b184897294f71fc511710106b52d1c6fa1efbf134c6c4ab8e38e73484dd0f16
|
|
7
|
+
data.tar.gz: 99f3ec9e1ea7d21c01b1622137573273422d56ac7dd7e6aa29cf7c9a8c0d6239dc37ba7419a365a530b87897fb6e42ebae229f465082a0d58832799817426570
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## [0.6.1] - 2026-06-18
|
|
2
|
+
|
|
3
|
+
- Include pre-release versions when fetching external gem info, so gems published only
|
|
4
|
+
as pre-releases (e.g. `typesense-rails`) are found and get a generated trailing
|
|
5
|
+
comment. Stable gems still resolve to their latest stable version.
|
|
6
|
+
|
|
7
|
+
## [0.6.0] - 2026-06-18
|
|
8
|
+
|
|
9
|
+
- Add support for the `install_if` directive. `install_if` blocks are now parsed and
|
|
10
|
+
formatted like other directive blocks (`group`, `path`, etc.).
|
|
11
|
+
- Fixed a crash when a block was empty.
|
|
12
|
+
- Fall back to a gem's `description` when its `summary` is `nil` or blank, so the
|
|
13
|
+
generated trailing comment is still populated.
|
|
14
|
+
- Declare `prism` and `zeitwerk` as runtime dependencies in the gemspec (they were
|
|
15
|
+
previously only in the Gemfile), fixing standalone installs of the gem.
|
|
16
|
+
- Internal refactoring (inlined the summarizer into `GemDeclaration`) and dependency updates.
|
|
17
|
+
|
|
1
18
|
## [0.5.1] - 2026-03-13
|
|
2
19
|
|
|
3
20
|
- Remove references to running as a bundler plugin. This does not work well, so decided to remove it.
|
data/README.md
CHANGED
|
@@ -10,19 +10,46 @@ Rebundler automatically reorders and annotates your Gemfile.
|
|
|
10
10
|
|
|
11
11
|
## Example
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Take this small Gemfile:
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
```ruby
|
|
16
|
+
source "https://rubygems.org"
|
|
17
|
+
|
|
18
|
+
gem "rails"
|
|
19
|
+
gem "puma"
|
|
20
|
+
gem "avo"
|
|
21
|
+
|
|
22
|
+
group :development do
|
|
23
|
+
gem "herb"
|
|
24
|
+
end
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
After running Rebundler, the gems are sorted, and each one is annotated with its description:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
source "https://rubygems.org"
|
|
31
|
+
|
|
32
|
+
gem "avo" # Admin panel framework and Content Management System for Ruby on Rails.
|
|
33
|
+
gem "puma" # A Ruby/Rack web server built for parallelism.
|
|
34
|
+
gem "rails" # Full-stack web application framework.
|
|
35
|
+
|
|
36
|
+
group :development do
|
|
37
|
+
gem "herb" # The modern HTML+ERB Toolchain
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Try it online
|
|
42
|
+
|
|
43
|
+
You can play around with Rebundler in the [Rebundler Playground](https://rebundler.dev/). Just paste your Gemfile and hit **Rebundle**. It encodes the Gemfile in a hash in the URL, so you can easily share the result with others.
|
|
18
44
|
|
|
19
45
|
## Known limitations
|
|
20
46
|
|
|
21
47
|
- **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.
|
|
48
|
+
- **Non-trailing comments are discarded.** Rebundler keeps existing trailing comments on `gem` lines, but standalone comments and commented-out gems are removed.
|
|
22
49
|
|
|
23
50
|
## Installation
|
|
24
51
|
|
|
25
|
-
|
|
52
|
+
Add it to your Gemfile with `bundle add rebundler`, or install it standalone with `gem install rebundler`. There are two ways to run Rebundler.
|
|
26
53
|
|
|
27
54
|
### 1. Writing mode
|
|
28
55
|
|
|
@@ -90,9 +117,19 @@ Pass `overwrite_comments: true` to replace existing trailing comments on `gem` l
|
|
|
90
117
|
new_content = parser.format(overwrite_comments: true)
|
|
91
118
|
```
|
|
92
119
|
|
|
120
|
+
## How it works
|
|
121
|
+
|
|
122
|
+
Since Gemfiles are just regular Ruby files, Rebundler parses them with [Prism](https://github.com/ruby/prism). It walks the AST using Prism's visitor pattern to find `gem` declarations and groups, while keeping generic setup methods like `source`, `ruby`, and `git_source` at the top.
|
|
123
|
+
|
|
124
|
+
Gems defined at the top level are grouped together. Gems inside blocks (like `group :development do ... end`) stay within their block, and duplicate groups are automatically merged. Within each group, gems are sorted alphabetically.
|
|
125
|
+
|
|
126
|
+
Prism is only a parser, not a formatter, so Rebundler reconstructs the Gemfile with some string manipulation. Because Prism records the exact line and column of every node, Rebundler copies the original source for each gem verbatim — which keeps your existing style intact. It then loads each gem's description from your local bundle (fetching it from RubyGems if it's missing) and appends it as a trailing comment.
|
|
127
|
+
|
|
128
|
+
By default, existing trailing comments are kept and other comments are discarded.
|
|
129
|
+
|
|
93
130
|
## Development
|
|
94
131
|
|
|
95
|
-
After checking out the repo, run `bundle install` to install dependencies. Then, run `
|
|
132
|
+
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.
|
|
96
133
|
|
|
97
134
|
## Contributing
|
|
98
135
|
|
|
@@ -1,15 +1,57 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Rebundler
|
|
4
|
-
GemDeclaration
|
|
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
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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
|
+
dependency = Gem::Dependency.new(name).tap { |d| d.prerelease = true } # Also find prelease gems
|
|
47
|
+
|
|
48
|
+
spec = Gem::SpecFetcher.fetcher.spec_for_dependency(dependency)
|
|
49
|
+
|
|
50
|
+
return if spec.nil? || spec.first.empty?
|
|
51
|
+
|
|
52
|
+
spec.first.first.first
|
|
53
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Timeout::Error => e
|
|
54
|
+
raise Rebundler::Error, "Network error while fetching gem info for '#{name}': #{e.message}"
|
|
13
55
|
end
|
|
14
56
|
end
|
|
15
57
|
end
|
data/lib/rebundler/gem_set.rb
CHANGED
|
@@ -16,7 +16,10 @@ module Rebundler
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def <=>(other)
|
|
19
|
-
|
|
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]
|
data/lib/rebundler/version.rb
CHANGED
data/lib/rebundler/visitor.rb
CHANGED
|
@@ -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 <<
|
|
14
|
+
@current_set.plugins << GemDeclaration.new(node:)
|
|
15
15
|
when :gem
|
|
16
|
-
@current_set.gems <<
|
|
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
|
@@ -8,5 +8,5 @@ module Rebundler
|
|
|
8
8
|
class Error < StandardError; end
|
|
9
9
|
|
|
10
10
|
SORTABLE_NODES = %i[plugin gem].freeze
|
|
11
|
-
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
|
|
12
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.
|
|
4
|
+
version: 0.6.1
|
|
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:
|
|
@@ -29,7 +57,6 @@ files:
|
|
|
29
57
|
- lib/rebundler/gem_set.rb
|
|
30
58
|
- lib/rebundler/parser.rb
|
|
31
59
|
- lib/rebundler/serializer.rb
|
|
32
|
-
- lib/rebundler/summarizer.rb
|
|
33
60
|
- lib/rebundler/version.rb
|
|
34
61
|
- lib/rebundler/visitor.rb
|
|
35
62
|
- mise.toml
|
|
@@ -55,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
55
82
|
- !ruby/object:Gem::Version
|
|
56
83
|
version: '0'
|
|
57
84
|
requirements: []
|
|
58
|
-
rubygems_version: 4.0.
|
|
85
|
+
rubygems_version: 4.0.10
|
|
59
86
|
specification_version: 4
|
|
60
87
|
summary: Rebundler makes your Gemfile look good.
|
|
61
88
|
test_files: []
|
data/lib/rebundler/summarizer.rb
DELETED
|
@@ -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
|