Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ Options:
--post, -a, [--postrequire=file] # A file to be required after Bundler.require is called
# Default: sorbet/tapioca/require.rb
-x, [--exclude=gem [gem ...]] # Exclude the given gem(s) from RBI generation
[--include-dependencies], [--no-include-dependencies] # Generate RBI files for dependencies of the given gem(s)
--typed, -t, [--typed-overrides=gem:level [gem:level ...]] # Override for typed sigils for generated gem RBIs
# Default: {"activesupport"=>"false"}
[--verify], [--no-verify] # Verify RBIs are up-to-date
Expand Down Expand Up @@ -199,7 +200,7 @@ generate RBIs from gems
```
<!-- END_HELP_COMMAND_GEM -->

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:
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. If you want to force the regeneration you can supply gem names to the `tapioca gem` command. When supplying gem names if you want to generate RBI files for their dependencies as well, you can use the `--include-dependencies` option. When changing Tapioca configuration or bumping its version, it may be useful to force the regeneration of all the RBI files previously generated. This can be done with the `--all` option:

```shell
bin/tapioca gems --all
Expand Down Expand Up @@ -904,6 +905,7 @@ gem:
prerequire: ''
postrequire: sorbet/tapioca/require.rb
exclude: []
include_dependencies: false
typed_overrides:
activesupport: 'false'
verify: false
Expand Down
11 changes: 10 additions & 1 deletion lib/tapioca/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ def dsl(*constant_or_paths)
banner: "gem [gem ...]",
desc: "Exclude the given gem(s) from RBI generation",
default: []
option :include_dependencies,
type: :boolean,
desc: "Generate RBI files for dependencies of the given gem(s)",
default: false
option :typed_overrides,
aliases: ["--typed", "-t"],
type: :hash,
Expand Down Expand Up @@ -251,17 +255,22 @@ def gem(*gems)

all = options[:all]
verify = options[:verify]
include_dependencies = options[:include_dependencies]

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

unless gems.empty?
if gems.empty?
raise MalformattedArgumentError,
"Option '--include-dependencies' must be provided with gems" if include_dependencies
else
raise MalformattedArgumentError, "Option '--all' must be provided without any other arguments" if all
raise MalformattedArgumentError, "Option '--verify' must be provided without any other arguments" if verify
end

command_args = {
Comment thread
LTe marked this conversation as resolved.
gem_names: all ? [] : gems,
exclude: options[:exclude],
include_dependencies: options[:include_dependencies],
prerequire: options[:prerequire],
postrequire: options[:postrequire],
typed_overrides: options[:typed_overrides],
Expand Down
25 changes: 23 additions & 2 deletions lib/tapioca/commands/abstract_gem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class AbstractGem < Command
params(
gem_names: T::Array[String],
exclude: T::Array[String],
include_dependencies: T::Boolean,
prerequire: T.nilable(String),
postrequire: String,
typed_overrides: T::Hash[String, String],
Expand All @@ -31,6 +32,7 @@ class AbstractGem < Command
def initialize(
gem_names:,
exclude:,
include_dependencies:,
prerequire:,
postrequire:,
typed_overrides:,
Expand All @@ -47,6 +49,7 @@ def initialize(
)
@gem_names = gem_names
@exclude = exclude
@include_dependencies = include_dependencies
@prerequire = prerequire
@postrequire = postrequire
@typed_overrides = typed_overrides
Expand Down Expand Up @@ -74,14 +77,32 @@ def initialize(
def gems_to_generate(gem_names)
return @bundle.dependencies if gem_names.empty?

gem_names.map do |gem_name|
gem_names.each_with_object([]) do |gem_name, gems|
gem = @bundle.gem(gem_name)

if gem.nil?
raise Thor::Error, set_color("Error: Cannot find gem '#{gem_name}'", :red)
end

gem
gems.concat(gem_dependencies(gem)) if @include_dependencies
gems << gem
end
end

sig do
params(
gem: Gemfile::GemSpec,
dependencies: T::Array[Gemfile::GemSpec],
).returns(T::Array[Gemfile::GemSpec])
end
def gem_dependencies(gem, dependencies = [])
direct_dependencies = gem.dependencies.filter_map { |dependency| @bundle.gem(dependency.name) }
gems = dependencies | direct_dependencies

if direct_dependencies.empty?
gems
else
direct_dependencies.reduce(gems) { |result, gem| gem_dependencies(gem, result) }
end
end

Expand Down
5 changes: 5 additions & 0 deletions lib/tapioca/gemfile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ def name
@spec.name
end

sig { returns(T::Array[::Gem::Dependency]) }
def dependencies
@spec.dependencies
end

sig { returns(String) }
def rbi_file_name
"#{name}@#{version}.rbi"
Expand Down
37 changes: 37 additions & 0 deletions spec/tapioca/cli/gem_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,17 @@ class << self
assert_empty_stdout(result)
refute_success_status(result)
end

it "must show an error if --include-dependencies is supplied without gem" do
result = @project.tapioca("gem --include-dependencies")

assert_equal(<<~ERR, result.err)
Option '--include-dependencies' must be provided with gems
ERR

assert_empty_stdout(result)
refute_success_status(result)
end
end

describe "generate" do
Expand Down Expand Up @@ -744,6 +755,32 @@ class Secret; end
assert_success_status(result)
end

it "must respect include-dependencies option" do
@project.require_real_gem("actionpack", "7.0.6")
@project.require_mock_gem(mock_gem("foo", "0.0.1"))
@project.require_mock_gem(mock_gem("bar", "0.3.0", dependencies: ["bundler", "actionpack"]))
@project.require_mock_gem(mock_gem("baz", "0.0.2"))
@project.bundle_install

result = @project.tapioca("gem foo bar --include-dependencies")

assert_stdout_includes(result, "Compiled foo")
assert_stdout_includes(result, "Compiled bar")
assert_stdout_includes(result, "Compiled actionpack")
assert_stdout_includes(result, "Compiled rack")
refute_includes(result.out, "Compiled baz")
refute_includes(result.out, "Compiled bundler")

assert_project_file_exist("sorbet/rbi/gems/foo@0.0.1.rbi")
assert_project_file_exist("sorbet/rbi/gems/bar@0.3.0.rbi")
assert_project_file_exist("sorbet/rbi/gems/actionpack@7.0.6.rbi")
assert_project_file_exist("sorbet/rbi/gems/rack@2.2.8.rbi")
refute_project_file_exist("sorbet/rbi/gems/baz@0.0.2.rbi")

assert_empty_stderr(result)
assert_success_status(result)
end

it "does not crash when the extras gem is loaded" do
foo = mock_gem("foo", "0.0.1") do
write("lib/foo.rb", FOO_RB)
Expand Down