Skip to content

Commit 22a4324

Browse files
Morriarparacycle
andcommitted
Extract gem loader
Signed-off-by: Alexandre Terrasa <alexandre.terrasa@shopify.com> Co-authored-by: Ufuk Kayserilioglu <ufuk.kayserilioglu@shopify.com>
1 parent bce2cfb commit 22a4324

File tree

5 files changed

+161
-183
lines changed

5 files changed

+161
-183
lines changed

lib/tapioca/commands/gem.rb

Lines changed: 19 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ def initialize(
5555

5656
super()
5757

58-
@loader = T.let(nil, T.nilable(Runtime::Loader))
59-
@bundle = T.let(nil, T.nilable(Gemfile))
58+
@bundle = T.let(Gemfile.new(exclude), Gemfile)
6059
@existing_rbis = T.let(nil, T.nilable(T::Hash[String, String]))
6160
@expected_rbis = T.let(nil, T.nilable(T::Hash[String, String]))
6261
@include_doc = T.let(include_doc, T::Boolean)
@@ -66,7 +65,12 @@ def initialize(
6665

6766
sig { override.void }
6867
def execute
69-
require_gem_file
68+
Loaders::Gem.load_application(
69+
bundle: @bundle,
70+
prerequire: @prerequire,
71+
postrequire: @postrequire,
72+
default_command: default_command(:require),
73+
)
7074

7175
gem_queue = gems_to_generate(@gem_names).reject { |gem| @exclude.include?(gem.name) }
7276
anything_done = [
@@ -87,7 +91,7 @@ def execute
8791
gem_dir: @outpath.to_s,
8892
dsl_dir: @dsl_dir,
8993
auto_strictness: @auto_strictness,
90-
gems: bundle.dependencies
94+
gems: @bundle.dependencies
9195
)
9296

9397
say("All operations performed in working directory.", [:green, :bold])
@@ -117,7 +121,7 @@ def sync(should_verify: false, exclude: [])
117121
gem_dir: @outpath.to_s,
118122
dsl_dir: @dsl_dir,
119123
auto_strictness: @auto_strictness,
120-
gems: bundle.dependencies
124+
gems: @bundle.dependencies
121125
)
122126

123127
say("All operations performed in working directory.", [:green, :bold])
@@ -131,42 +135,12 @@ def sync(should_verify: false, exclude: [])
131135

132136
private
133137

134-
sig { returns(Runtime::Loader) }
135-
def loader
136-
@loader ||= Runtime::Loader.new
137-
end
138-
139-
sig { returns(Gemfile) }
140-
def bundle
141-
@bundle ||= Gemfile.new(@exclude)
142-
end
143-
144-
sig { void }
145-
def require_gem_file
146-
say("Requiring all gems to prepare for compiling... ")
147-
begin
148-
loader.load_bundle(bundle, @prerequire, @postrequire)
149-
rescue LoadError => e
150-
explain_failed_require(@postrequire, e)
151-
exit(1)
152-
end
153-
154-
Runtime::Trackers::Autoload.eager_load_all!
155-
156-
say(" Done", :green)
157-
unless bundle.missing_specs.empty?
158-
say(" completed with missing specs: ")
159-
say(bundle.missing_specs.join(", "), :yellow)
160-
end
161-
puts
162-
end
163-
164138
sig { params(gem_names: T::Array[String]).returns(T::Array[Gemfile::GemSpec]) }
165139
def gems_to_generate(gem_names)
166-
return bundle.dependencies if gem_names.empty?
140+
return @bundle.dependencies if gem_names.empty?
167141

168142
gem_names.map do |gem_name|
169-
gem = bundle.gem(gem_name)
143+
gem = @bundle.gem(gem_name)
170144
if gem.nil?
171145
say("Error: Cannot find gem '#{gem_name}'", :red)
172146
exit(1)
@@ -263,7 +237,12 @@ def perform_additions
263237
if gems.empty?
264238
say("Nothing to do.")
265239
else
266-
require_gem_file
240+
Loaders::Gem.load_application(
241+
bundle: @bundle,
242+
prerequire: @prerequire,
243+
postrequire: @postrequire,
244+
default_command: default_command(:require),
245+
)
267246

268247
Executor.new(gems, number_of_workers: @number_of_workers).run_in_parallel do |gem_name|
269248
filename = expected_rbi(gem_name)
@@ -273,7 +252,7 @@ def perform_additions
273252
move(old_filename, filename) unless old_filename == filename
274253
end
275254

276-
gem = T.must(bundle.gem(gem_name))
255+
gem = T.must(@bundle.gem(gem_name))
277256
compile_gem_rbi(gem)
278257
puts
279258
end
@@ -287,17 +266,6 @@ def perform_additions
287266
anything_done
288267
end
289268

290-
sig { params(file: String, error: LoadError).void }
291-
def explain_failed_require(file, error)
292-
say_error("\n\nLoadError: #{error}", :bold, :red)
293-
say_error("\nTapioca could not load all the gems required by your application.", :yellow)
294-
say_error("If you populated ", :yellow)
295-
say_error("#{file} ", :bold, :blue)
296-
say_error("with ", :yellow)
297-
say_error("`#{default_command(:require)}`", :bold, :blue)
298-
say_error("you should probably review it and remove the faulty line.", :yellow)
299-
end
300-
301269
sig { returns(T::Array[String]) }
302270
def removed_rbis
303271
(existing_rbis.keys - expected_rbis.keys).sort
@@ -359,7 +327,7 @@ def existing_rbis
359327

360328
sig { returns(T::Hash[String, String]) }
361329
def expected_rbis
362-
@expected_rbis ||= bundle.dependencies
330+
@expected_rbis ||= @bundle.dependencies
363331
.reject { |gem| @exclude.include?(gem.name) }
364332
.to_h { |gem| [gem.name, gem.version.to_s] }
365333
end

lib/tapioca/internal.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
require "tapioca/runtime/dynamic_mixin_compiler"
2929
require "tapioca/helpers/gem_helper"
30-
require "tapioca/runtime/loader"
3130

3231
require "tapioca/helpers/sorbet_helper"
3332
require "tapioca/helpers/rbi_helper"
@@ -51,6 +50,7 @@
5150
require "tapioca/static/requires_compiler"
5251

5352
require "tapioca/loaders/loader"
53+
require "tapioca/loaders/gem"
5454
require "tapioca/loaders/dsl"
5555

5656
require "tapioca/gem"

lib/tapioca/loaders/gem.rb

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
module Tapioca
5+
module Loaders
6+
class Gem < Loader
7+
extend T::Sig
8+
9+
sig do
10+
params(
11+
bundle: Gemfile,
12+
prerequire: T.nilable(String),
13+
postrequire: String,
14+
default_command: String
15+
).void
16+
end
17+
def self.load_application(bundle:, prerequire:, postrequire:, default_command:)
18+
loader = new(bundle: bundle, prerequire: prerequire, postrequire: postrequire, default_command: default_command)
19+
loader.load
20+
end
21+
22+
sig { override.void }
23+
def load
24+
require_gem_file
25+
end
26+
27+
protected
28+
29+
sig do
30+
params(
31+
bundle: Gemfile,
32+
prerequire: T.nilable(String),
33+
postrequire: String,
34+
default_command: String
35+
).void
36+
end
37+
def initialize(bundle:, prerequire:, postrequire:, default_command:)
38+
super()
39+
40+
@bundle = bundle
41+
@prerequire = prerequire
42+
@postrequire = postrequire
43+
@default_command = default_command
44+
end
45+
46+
sig { void }
47+
def require_gem_file
48+
say("Requiring all gems to prepare for compiling... ")
49+
begin
50+
load_bundle(@bundle, @prerequire, @postrequire)
51+
rescue LoadError => e
52+
explain_failed_require(@postrequire, e)
53+
exit(1)
54+
end
55+
56+
Runtime::Trackers::Autoload.eager_load_all!
57+
58+
say(" Done", :green)
59+
unless @bundle.missing_specs.empty?
60+
say(" completed with missing specs: ")
61+
say(@bundle.missing_specs.join(", "), :yellow)
62+
end
63+
puts
64+
end
65+
66+
sig { params(file: String, error: LoadError).void }
67+
def explain_failed_require(file, error)
68+
say_error("\n\nLoadError: #{error}", :bold, :red)
69+
say_error("\nTapioca could not load all the gems required by your application.", :yellow)
70+
say_error("If you populated ", :yellow)
71+
say_error("#{file} ", :bold, :blue)
72+
say_error("with ", :yellow)
73+
say_error("`#{@default_command}`", :bold, :blue)
74+
say_error("you should probably review it and remove the faulty line.", :yellow)
75+
end
76+
end
77+
end
78+
end

lib/tapioca/loaders/loader.rb

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class Loader
99

1010
include Thor::Base
1111
include CliHelper
12+
include Tapioca::GemHelper
1213

1314
abstract!
1415

@@ -17,6 +18,21 @@ def load; end
1718

1819
private
1920

21+
sig do
22+
params(gemfile: Tapioca::Gemfile, initialize_file: T.nilable(String), require_file: T.nilable(String)).void
23+
end
24+
def load_bundle(gemfile, initialize_file, require_file)
25+
require_helper(initialize_file)
26+
27+
load_rails_application
28+
29+
gemfile.require_bundle
30+
31+
require_helper(require_file)
32+
33+
load_rails_engines
34+
end
35+
2036
sig { params(environment_load: T::Boolean, eager_load: T::Boolean).void }
2137
def load_rails_application(environment_load: false, eager_load: false)
2238
return unless File.exist?("config/application.rb")
@@ -32,6 +48,43 @@ def load_rails_application(environment_load: false, eager_load: false)
3248
eager_load_rails_app if eager_load
3349
end
3450

51+
sig { void }
52+
def load_rails_engines
53+
rails_engines.each do |engine|
54+
errored_files = []
55+
56+
engine.config.eager_load_paths.each do |load_path|
57+
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
58+
require(file)
59+
rescue LoadError, StandardError
60+
errored_files << file
61+
end
62+
end
63+
64+
# Try files that have errored one more time
65+
# It might have been a load order problem
66+
errored_files.each do |file|
67+
require(file)
68+
rescue LoadError, StandardError
69+
nil
70+
end
71+
end
72+
end
73+
74+
sig { returns(T::Array[T.untyped]) }
75+
def rails_engines
76+
return [] unless Object.const_defined?("Rails::Engine")
77+
78+
safe_require("active_support/core_ext/class/subclasses")
79+
80+
project_path = Bundler.default_gemfile.parent.expand_path
81+
# We can use `Class#descendants` here, since we know Rails is loaded
82+
Object.const_get("Rails::Engine")
83+
.descendants
84+
.reject(&:abstract_railtie?)
85+
.reject { |engine| gem_in_app_dir?(project_path, engine.config.root.to_path) }
86+
end
87+
3588
sig { params(path: String).void }
3689
def safe_require(path)
3790
require path
@@ -72,6 +125,16 @@ def eager_load_rails_app
72125
application.config.eager_load_namespaces.each(&:eager_load!)
73126
end
74127
end
128+
129+
sig { params(file: T.nilable(String)).void }
130+
def require_helper(file)
131+
return unless file
132+
133+
file = File.absolute_path(file)
134+
return unless File.exist?(file)
135+
136+
require(file)
137+
end
75138
end
76139
end
77140
end

0 commit comments

Comments
 (0)