Skip to content

Commit bef007b

Browse files
committed
Implement a thing
1 parent f3e2850 commit bef007b

5 files changed

Lines changed: 85 additions & 3 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ Options:
173173
--post, -a, [--postrequire=file] # A file to be required after Bundler.require is called
174174
# Default: sorbet/tapioca/require.rb
175175
-x, [--exclude=gem [gem ...]] # Exclude the given gem(s) from RBI generation
176+
[--include-dependencies], [--no-include-dependencies] # Generate RBI files for dependencies of the given gem(s)
176177
--typed, -t, [--typed-overrides=gem:level [gem:level ...]] # Override for typed sigils for generated gem RBIs
177178
# Default: {"activesupport"=>"false"}
178179
[--verify], [--no-verify] # Verify RBIs are up-to-date
@@ -201,7 +202,7 @@ generate RBIs from gems
201202
```
202203
<!-- END_HELP_COMMAND_GEM -->
203204
204-
By default, running `tapioca gem` will only generate the RBI files for gems that have been added to or removed from the project's `Gemfile` this means that Tapioca will not regenerate the RBI files for untouched gems. However, when changing Tapioca configuration or bumping its version, it may be useful to force the regeneration of the RBI files previously generated. This can be done with the `--all` option:
205+
By default, running `tapioca gem` will only generate the RBI files for gems that have been added to or removed from the project's `Gemfile` this means that Tapioca will not regenerate the RBI files for untouched gems. When supplying gem names if you want to generate RBI files for their dependencies as well, you can use the `--include-dependencies` option. However, when changing Tapioca configuration or bumping its version, it may be useful to force the regeneration of the RBI files previously generated. This can be done with the `--all` option:
205206
206207
```shell
207208
bin/tapioca gems --all
@@ -944,6 +945,7 @@ gem:
944945
prerequire: ''
945946
postrequire: sorbet/tapioca/require.rb
946947
exclude: []
948+
include_dependencies: false
947949
typed_overrides:
948950
activesupport: 'false'
949951
verify: false

lib/tapioca/cli.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ def dsl(*constant_or_paths)
193193
banner: "gem [gem ...]",
194194
desc: "Exclude the given gem(s) from RBI generation",
195195
default: []
196+
option :include_dependencies,
197+
type: :boolean,
198+
desc: "Generate RBI files for dependencies of the given gem(s)",
199+
default: false
196200
option :typed_overrides,
197201
aliases: ["--typed", "-t"],
198202
type: :hash,
@@ -246,17 +250,22 @@ def gem(*gems)
246250

247251
all = options[:all]
248252
verify = options[:verify]
253+
include_dependencies = options[:include_dependencies]
249254

250255
raise MalformattedArgumentError, "Options '--all' and '--verify' are mutually exclusive" if all && verify
251256

252-
unless gems.empty?
257+
if gems.empty?
258+
raise MalformattedArgumentError,
259+
"Option '--include-dependencies' must be provided with gems" if include_dependencies
260+
else
253261
raise MalformattedArgumentError, "Option '--all' must be provided without any other arguments" if all
254262
raise MalformattedArgumentError, "Option '--verify' must be provided without any other arguments" if verify
255263
end
256264

257265
command_args = {
258266
gem_names: all ? [] : gems,
259267
exclude: options[:exclude],
268+
include_dependencies: options[:include_dependencies],
260269
prerequire: options[:prerequire],
261270
postrequire: options[:postrequire],
262271
typed_overrides: options[:typed_overrides],

lib/tapioca/commands/abstract_gem.rb

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class AbstractGem < Command
1313
params(
1414
gem_names: T::Array[String],
1515
exclude: T::Array[String],
16+
include_dependencies: T::Boolean,
1617
prerequire: T.nilable(String),
1718
postrequire: String,
1819
typed_overrides: T::Hash[String, String],
@@ -31,6 +32,7 @@ class AbstractGem < Command
3132
def initialize(
3233
gem_names:,
3334
exclude:,
35+
include_dependencies:,
3436
prerequire:,
3537
postrequire:,
3638
typed_overrides:,
@@ -47,6 +49,7 @@ def initialize(
4749
)
4850
@gem_names = gem_names
4951
@exclude = exclude
52+
@include_dependencies = include_dependencies
5053
@prerequire = prerequire
5154
@postrequire = postrequire
5255
@typed_overrides = typed_overrides
@@ -74,6 +77,7 @@ def initialize(
7477
def gems_to_generate(gem_names)
7578
return @bundle.dependencies if gem_names.empty?
7679

80+
gem_names = gems_and_their_dependencies(gem_names) if @include_dependencies
7781
gem_names.map do |gem_name|
7882
gem = @bundle.gem(gem_name)
7983

@@ -82,7 +86,12 @@ def gems_to_generate(gem_names)
8286
end
8387

8488
gem
85-
end
89+
end.flatten.compact
90+
end
91+
92+
sig { params(gem_names: T::Array[String]).returns(T::Array[String]) }
93+
def gems_and_their_dependencies(gem_names)
94+
gem_names.map { |gem_name| gem_with_dependencies(gem_name) }.flatten
8695
end
8796

8897
sig { params(gem: Gemfile::GemSpec).void }
@@ -252,6 +261,26 @@ def expected_rbis
252261
.to_h { |gem| [gem.name, gem.version.to_s] }
253262
end
254263

264+
sig do
265+
params(
266+
gem_name: String,
267+
gem_names: T::Array[String],
268+
).returns(T::Array[String])
269+
end
270+
def gem_with_dependencies(gem_name, gem_names = [])
271+
gem = @bundle.gem(gem_name)
272+
return [gem_name] if gem.nil?
273+
274+
direct_dependencies = gem.dependencies.map { |gem_spec| @bundle.gem(gem_spec.name) }.compact.map(&:name)
275+
gem_names = [gem.name, gem_names, direct_dependencies].flatten.uniq.compact
276+
277+
return gem_names if direct_dependencies.empty?
278+
279+
direct_dependencies.reduce(gem_names) do |result, name|
280+
gem_with_dependencies(name, result)
281+
end
282+
end
283+
255284
sig { params(gem_name: String, version: String).returns(Pathname) }
256285
def gem_rbi_filename(gem_name, version)
257286
@outpath / "#{gem_name}@#{version}.rbi"

lib/tapioca/gemfile.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ def name
156156
@spec.name
157157
end
158158

159+
sig { returns(T::Array[::Gem::Dependency]) }
160+
def dependencies
161+
@spec.dependencies
162+
end
163+
159164
sig { returns(String) }
160165
def rbi_file_name
161166
"#{name}@#{version}.rbi"

spec/tapioca/cli/gem_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,17 @@ class << self
198198
assert_empty_stdout(result)
199199
refute_success_status(result)
200200
end
201+
202+
it "must show an error if --include-dependencies is supplied without gem" do
203+
result = @project.tapioca("gem --include-dependencies")
204+
205+
assert_equal(<<~ERR, result.err)
206+
Option '--include-dependencies' must be provided with gems
207+
ERR
208+
209+
assert_empty_stdout(result)
210+
refute_success_status(result)
211+
end
201212
end
202213

203214
describe "generate" do
@@ -744,6 +755,32 @@ class Secret; end
744755
assert_success_status(result)
745756
end
746757

758+
it "must respect include-dependencies option" do
759+
@project.require_real_gem("actionpack", "7.0.6")
760+
@project.require_mock_gem(mock_gem("foo", "0.0.1"))
761+
@project.require_mock_gem(mock_gem("bar", "0.3.0", dependencies: ["bundler", "actionpack"]))
762+
@project.require_mock_gem(mock_gem("baz", "0.0.2"))
763+
@project.bundle_install
764+
765+
result = @project.tapioca("gem foo bar --include-dependencies")
766+
767+
assert_stdout_includes(result, "Compiled foo")
768+
assert_stdout_includes(result, "Compiled bar")
769+
assert_stdout_includes(result, "Compiled actionpack")
770+
assert_stdout_includes(result, "Compiled rack")
771+
refute_includes(result.out, "Compiled baz")
772+
refute_includes(result.out, "Compiled bundler")
773+
774+
assert_project_file_exist("sorbet/rbi/gems/foo@0.0.1.rbi")
775+
assert_project_file_exist("sorbet/rbi/gems/bar@0.3.0.rbi")
776+
assert_project_file_exist("sorbet/rbi/gems/actionpack@7.0.6.rbi")
777+
assert_project_file_exist("sorbet/rbi/gems/rack@2.2.8.rbi")
778+
refute_project_file_exist("sorbet/rbi/gems/baz@0.0.2.rbi")
779+
780+
assert_empty_stderr(result)
781+
assert_success_status(result)
782+
end
783+
747784
it "does not crash when the extras gem is loaded" do
748785
foo = mock_gem("foo", "0.0.1") do
749786
write("lib/foo.rb", FOO_RB)

0 commit comments

Comments
 (0)