tapioca 0.4.0 → 0.4.5
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/Gemfile +26 -1
- data/README.md +16 -0
- data/Rakefile +16 -4
- data/lib/tapioca.rb +6 -2
- data/lib/tapioca/cli.rb +25 -3
- data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +130 -0
- data/lib/tapioca/compilers/dsl/action_mailer.rb +65 -0
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +267 -0
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +404 -0
- data/lib/tapioca/compilers/dsl/active_record_enum.rb +112 -0
- data/lib/tapioca/compilers/dsl/active_record_identity_cache.rb +212 -0
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +100 -0
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +168 -0
- data/lib/tapioca/compilers/dsl/active_resource.rb +140 -0
- data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +126 -0
- data/lib/tapioca/compilers/dsl/base.rb +165 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +96 -0
- data/lib/tapioca/compilers/dsl/protobuf.rb +144 -0
- data/lib/tapioca/compilers/dsl/smart_properties.rb +173 -0
- data/lib/tapioca/compilers/dsl/state_machines.rb +378 -0
- data/lib/tapioca/compilers/dsl/url_helpers.rb +160 -0
- data/lib/tapioca/compilers/dsl_compiler.rb +121 -0
- data/lib/tapioca/compilers/requires_compiler.rb +67 -0
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +195 -32
- data/lib/tapioca/config.rb +11 -6
- data/lib/tapioca/config_builder.rb +19 -9
- data/lib/tapioca/constant_locator.rb +1 -0
- data/lib/tapioca/core_ext/class.rb +23 -0
- data/lib/tapioca/gemfile.rb +32 -9
- data/lib/tapioca/generator.rb +200 -24
- data/lib/tapioca/loader.rb +30 -9
- data/lib/tapioca/version.rb +1 -1
- metadata +31 -40
data/lib/tapioca/config.rb
CHANGED
@@ -12,6 +12,7 @@ module Tapioca
|
|
12
12
|
const(:exclude, T::Array[String])
|
13
13
|
const(:typed_overrides, T::Hash[String, String])
|
14
14
|
const(:todos_path, String)
|
15
|
+
const(:generators, T::Array[String])
|
15
16
|
|
16
17
|
sig { returns(Pathname) }
|
17
18
|
def outpath
|
@@ -21,17 +22,21 @@ module Tapioca
|
|
21
22
|
|
22
23
|
private_class_method :new
|
23
24
|
|
24
|
-
|
25
|
-
SORBET_CONFIG = "
|
25
|
+
SORBET_PATH = T.let("sorbet", String)
|
26
|
+
SORBET_CONFIG = T.let("#{SORBET_PATH}/config", String)
|
27
|
+
TAPIOCA_PATH = T.let("#{SORBET_PATH}/tapioca", String)
|
28
|
+
TAPIOCA_CONFIG = T.let("#{TAPIOCA_PATH}/config.yml", String)
|
29
|
+
|
30
|
+
DEFAULT_POSTREQUIRE = T.let("#{TAPIOCA_PATH}/require.rb", String)
|
31
|
+
DEFAULT_RBIDIR = T.let("#{SORBET_PATH}/rbi", String)
|
32
|
+
DEFAULT_DSLDIR = T.let("#{DEFAULT_RBIDIR}/dsl", String)
|
33
|
+
DEFAULT_GEMDIR = T.let("#{DEFAULT_RBIDIR}/gems", String)
|
34
|
+
DEFAULT_TODOSPATH = T.let("#{DEFAULT_RBIDIR}/todo.rbi", String)
|
26
35
|
|
27
|
-
DEFAULT_POSTREQUIRE = "sorbet/tapioca/require.rb"
|
28
|
-
DEFAULT_RBIDIR = "sorbet/rbi"
|
29
|
-
DEFAULT_OUTDIR = T.let("#{DEFAULT_RBIDIR}/gems", String)
|
30
36
|
DEFAULT_OVERRIDES = T.let({
|
31
37
|
# ActiveSupport overrides some core methods with different signatures
|
32
38
|
# so we generate a typed: false RBI for it to suppress errors
|
33
39
|
"activesupport" => "false",
|
34
40
|
}.freeze, T::Hash[String, String])
|
35
|
-
DEFAULT_TODOSPATH = T.let("#{DEFAULT_RBIDIR}/todo.rbi", String)
|
36
41
|
end
|
37
42
|
end
|
@@ -8,10 +8,10 @@ module Tapioca
|
|
8
8
|
class << self
|
9
9
|
extend(T::Sig)
|
10
10
|
|
11
|
-
sig { params(options: T::Hash[String, T.untyped]).returns(Config) }
|
12
|
-
def from_options(options)
|
11
|
+
sig { params(command: Symbol, options: T::Hash[String, T.untyped]).returns(Config) }
|
12
|
+
def from_options(command, options)
|
13
13
|
Config.from_hash(
|
14
|
-
merge_options(default_options, config_options, options)
|
14
|
+
merge_options(default_options(command), config_options, options)
|
15
15
|
)
|
16
16
|
end
|
17
17
|
|
@@ -19,16 +19,25 @@ module Tapioca
|
|
19
19
|
|
20
20
|
sig { returns(T::Hash[String, T.untyped]) }
|
21
21
|
def config_options
|
22
|
-
if File.exist?(Config::
|
23
|
-
YAML.load_file(Config::
|
22
|
+
if File.exist?(Config::TAPIOCA_CONFIG)
|
23
|
+
YAML.load_file(Config::TAPIOCA_CONFIG, fallback: {})
|
24
24
|
else
|
25
25
|
{}
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
sig { returns(T::Hash[String, T.untyped]) }
|
30
|
-
def default_options
|
31
|
-
|
29
|
+
sig { params(command: Symbol).returns(T::Hash[String, T.untyped]) }
|
30
|
+
def default_options(command)
|
31
|
+
default_outdir = case command
|
32
|
+
when :sync, :generate
|
33
|
+
Config::DEFAULT_GEMDIR
|
34
|
+
when :dsl
|
35
|
+
Config::DEFAULT_DSLDIR
|
36
|
+
else
|
37
|
+
Config::SORBET_PATH
|
38
|
+
end
|
39
|
+
|
40
|
+
DEFAULT_OPTIONS.merge("outdir" => default_outdir)
|
32
41
|
end
|
33
42
|
|
34
43
|
sig { returns(String) }
|
@@ -55,11 +64,12 @@ module Tapioca
|
|
55
64
|
|
56
65
|
DEFAULT_OPTIONS = T.let({
|
57
66
|
"postrequire" => Config::DEFAULT_POSTREQUIRE,
|
58
|
-
"outdir" =>
|
67
|
+
"outdir" => nil,
|
59
68
|
"generate_command" => default_command,
|
60
69
|
"exclude" => [],
|
61
70
|
"typed_overrides" => Config::DEFAULT_OVERRIDES,
|
62
71
|
"todos_path" => Config::DEFAULT_TODOSPATH,
|
72
|
+
"generators" => [],
|
63
73
|
}.freeze, T::Hash[String, T.untyped])
|
64
74
|
end
|
65
75
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class Class
|
5
|
+
# Returns an array with all classes that are < than its receiver.
|
6
|
+
#
|
7
|
+
# class C; end
|
8
|
+
# C.descendants # => []
|
9
|
+
#
|
10
|
+
# class B < C; end
|
11
|
+
# C.descendants # => [B]
|
12
|
+
#
|
13
|
+
# class A < B; end
|
14
|
+
# C.descendants # => [B, A]
|
15
|
+
#
|
16
|
+
# class D < C; end
|
17
|
+
# C.descendants # => [B, A, D]
|
18
|
+
def descendants
|
19
|
+
ObjectSpace.each_object(singleton_class).reject do |k|
|
20
|
+
k.singleton_class? || k == self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/tapioca/gemfile.rb
CHANGED
@@ -28,7 +28,7 @@ module Tapioca
|
|
28
28
|
sig { returns(T::Array[Gem]) }
|
29
29
|
def dependencies
|
30
30
|
@dependencies ||= begin
|
31
|
-
specs = definition.specs.to_a
|
31
|
+
specs = definition.locked_gems.specs.to_a
|
32
32
|
|
33
33
|
definition
|
34
34
|
.resolve
|
@@ -79,17 +79,18 @@ module Tapioca
|
|
79
79
|
extend(T::Sig)
|
80
80
|
|
81
81
|
IGNORED_GEMS = T.let(%w{
|
82
|
-
sorbet sorbet-static sorbet-runtime
|
82
|
+
sorbet sorbet-static sorbet-runtime
|
83
83
|
}.freeze, T::Array[String])
|
84
84
|
|
85
85
|
sig { returns(String) }
|
86
|
-
attr_reader :full_gem_path
|
86
|
+
attr_reader :full_gem_path, :version
|
87
87
|
|
88
88
|
sig { params(spec: Spec).void }
|
89
89
|
def initialize(spec)
|
90
90
|
@spec = T.let(spec, Tapioca::Gemfile::Spec)
|
91
91
|
real_gem_path = to_realpath(@spec.full_gem_path)
|
92
92
|
@full_gem_path = T.let(real_gem_path, String)
|
93
|
+
@version = T.let(version_string, String)
|
93
94
|
end
|
94
95
|
|
95
96
|
sig { params(gemfile_dir: String).returns(T::Boolean) }
|
@@ -109,11 +110,6 @@ module Tapioca
|
|
109
110
|
@spec.name
|
110
111
|
end
|
111
112
|
|
112
|
-
sig { returns(::Gem::Version) }
|
113
|
-
def version
|
114
|
-
@spec.version
|
115
|
-
end
|
116
|
-
|
117
113
|
sig { returns(String) }
|
118
114
|
def rbi_file_name
|
119
115
|
"#{name}@#{version}.rbi"
|
@@ -121,11 +117,38 @@ module Tapioca
|
|
121
117
|
|
122
118
|
sig { params(path: String).returns(T::Boolean) }
|
123
119
|
def contains_path?(path)
|
124
|
-
to_realpath(path).start_with?(full_gem_path)
|
120
|
+
to_realpath(path).start_with?(full_gem_path) || has_parent_gemspec?(path)
|
125
121
|
end
|
126
122
|
|
127
123
|
private
|
128
124
|
|
125
|
+
sig { returns(String) }
|
126
|
+
def version_string
|
127
|
+
version = @spec.version.to_s
|
128
|
+
version += "-#{@spec.source.revision}" if Bundler::Source::Git === @spec.source
|
129
|
+
version
|
130
|
+
end
|
131
|
+
|
132
|
+
sig { params(path: String).returns(T::Boolean) }
|
133
|
+
def has_parent_gemspec?(path)
|
134
|
+
# For some Git installed gems the location of the loaded file can
|
135
|
+
# be different from the gem path as indicated by the spec file
|
136
|
+
#
|
137
|
+
# To compensate for these cases, we walk up the directory hierarchy
|
138
|
+
# from the given file and try to match a <gem-name.gemspec> file in
|
139
|
+
# one of those folders to see if the path really belongs in the given gem
|
140
|
+
# or not.
|
141
|
+
return false unless Bundler::Source::Git === @spec.source
|
142
|
+
parent = Pathname.new(path)
|
143
|
+
|
144
|
+
until parent.root?
|
145
|
+
parent = parent.parent.expand_path
|
146
|
+
return true if parent.join("#{name}.gemspec").file?
|
147
|
+
end
|
148
|
+
|
149
|
+
false
|
150
|
+
end
|
151
|
+
|
129
152
|
sig { params(path: T.any(String, Pathname)).returns(String) }
|
130
153
|
def to_realpath(path)
|
131
154
|
path_string = path.to_s
|
data/lib/tapioca/generator.rb
CHANGED
@@ -35,7 +35,7 @@ module Tapioca
|
|
35
35
|
.each do |gem|
|
36
36
|
say("Processing '#{gem.name}' gem:", :green)
|
37
37
|
indent do
|
38
|
-
|
38
|
+
compile_gem_rbi(gem)
|
39
39
|
puts
|
40
40
|
end
|
41
41
|
end
|
@@ -44,6 +44,42 @@ module Tapioca
|
|
44
44
|
say("Please review changes and commit them.", [:green, :bold])
|
45
45
|
end
|
46
46
|
|
47
|
+
sig { void }
|
48
|
+
def build_requires
|
49
|
+
requires_path = Config::DEFAULT_POSTREQUIRE
|
50
|
+
compiler = Compilers::RequiresCompiler.new(Config::SORBET_CONFIG)
|
51
|
+
name = set_color(requires_path, :yellow, :bold)
|
52
|
+
say("Compiling #{name}, this may take a few seconds... ")
|
53
|
+
|
54
|
+
rb_string = compiler.compile
|
55
|
+
if rb_string.empty?
|
56
|
+
say("Nothing to do", :green)
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
# Clean all existing requires before regenerating the list so we update
|
61
|
+
# it with the new one found in the client code and remove the old ones.
|
62
|
+
File.delete(requires_path) if File.exist?(requires_path)
|
63
|
+
|
64
|
+
content = String.new
|
65
|
+
content << rbi_header(
|
66
|
+
config.generate_command,
|
67
|
+
reason: "explicit gem requires",
|
68
|
+
strictness: "false"
|
69
|
+
)
|
70
|
+
content << rb_string
|
71
|
+
|
72
|
+
outdir = File.dirname(requires_path)
|
73
|
+
FileUtils.mkdir_p(outdir)
|
74
|
+
File.write(requires_path, content)
|
75
|
+
|
76
|
+
say("Done", :green)
|
77
|
+
|
78
|
+
say("All requires from this application have been written to #{name}.", [:green, :bold])
|
79
|
+
cmd = set_color("tapioca sync", :yellow, :bold)
|
80
|
+
say("Please review changes and commit them, then run #{cmd}.", [:green, :bold])
|
81
|
+
end
|
82
|
+
|
47
83
|
sig { void }
|
48
84
|
def build_todos
|
49
85
|
todos_path = config.todos_path
|
@@ -62,7 +98,11 @@ module Tapioca
|
|
62
98
|
end
|
63
99
|
|
64
100
|
content = String.new
|
65
|
-
content << rbi_header(
|
101
|
+
content << rbi_header(
|
102
|
+
config.generate_command,
|
103
|
+
reason: "unresolved constants",
|
104
|
+
strictness: "false"
|
105
|
+
)
|
66
106
|
content << rbi_string
|
67
107
|
content << "\n"
|
68
108
|
|
@@ -76,6 +116,33 @@ module Tapioca
|
|
76
116
|
say("Please review changes and commit them.", [:green, :bold])
|
77
117
|
end
|
78
118
|
|
119
|
+
sig { params(requested_constants: T::Array[String]).void }
|
120
|
+
def build_dsl(requested_constants)
|
121
|
+
load_application(eager_load: requested_constants.empty?)
|
122
|
+
load_dsl_generators
|
123
|
+
|
124
|
+
say("Compiling DSL RBI files...")
|
125
|
+
say("")
|
126
|
+
|
127
|
+
compiler = Compilers::DslCompiler.new(
|
128
|
+
requested_constants: constantize(requested_constants),
|
129
|
+
requested_generators: config.generators,
|
130
|
+
error_handler: ->(error) {
|
131
|
+
say_error(error, :bold, :red)
|
132
|
+
}
|
133
|
+
)
|
134
|
+
|
135
|
+
compiler.run do |constant, contents|
|
136
|
+
compile_dsl_rbi(constant, contents)
|
137
|
+
end
|
138
|
+
|
139
|
+
say("")
|
140
|
+
say("Done", :green)
|
141
|
+
|
142
|
+
say("All operations performed in working directory.", [:green, :bold])
|
143
|
+
say("Please review changes and commit them.", [:green, :bold])
|
144
|
+
end
|
145
|
+
|
79
146
|
sig { void }
|
80
147
|
def sync_rbis_with_gemfile
|
81
148
|
anything_done = [
|
@@ -95,6 +162,11 @@ module Tapioca
|
|
95
162
|
|
96
163
|
private
|
97
164
|
|
165
|
+
EMPTY_RBI_COMMENT = <<~CONTENT
|
166
|
+
# THIS IS AN EMPTY RBI FILE.
|
167
|
+
# see https://github.com/Shopify/tapioca/blob/master/README.md#manual-gem-requires
|
168
|
+
CONTENT
|
169
|
+
|
98
170
|
sig { returns(Gemfile) }
|
99
171
|
def bundle
|
100
172
|
@bundle ||= Gemfile.new
|
@@ -113,15 +185,83 @@ module Tapioca
|
|
113
185
|
sig { void }
|
114
186
|
def require_gem_file
|
115
187
|
say("Requiring all gems to prepare for compiling... ")
|
116
|
-
|
188
|
+
begin
|
189
|
+
loader.load_bundle(config.prerequire, config.postrequire)
|
190
|
+
rescue LoadError => e
|
191
|
+
explain_failed_require(config.postrequire, e)
|
192
|
+
exit(1)
|
193
|
+
end
|
117
194
|
say(" Done", :green)
|
118
195
|
puts
|
119
196
|
end
|
120
197
|
|
198
|
+
sig { params(file: String, error: LoadError).void }
|
199
|
+
def explain_failed_require(file, error)
|
200
|
+
say_error("\n\nLoadError: #{error}", :bold, :red)
|
201
|
+
say_error("\nTapioca could not load all the gems required by your application.", :yellow)
|
202
|
+
say_error("If you populated ", :yellow)
|
203
|
+
say_error("#{file} ", :bold, :blue)
|
204
|
+
say_error("with ", :yellow)
|
205
|
+
say_error("tapioca require", :bold, :blue)
|
206
|
+
say_error("you should probably review it and remove the faulty line.", :yellow)
|
207
|
+
end
|
208
|
+
|
209
|
+
sig do
|
210
|
+
params(
|
211
|
+
message: String,
|
212
|
+
color: T.any(Symbol, T::Array[Symbol]),
|
213
|
+
).void
|
214
|
+
end
|
215
|
+
def say_error(message = "", *color)
|
216
|
+
force_new_line = (message.to_s !~ /( |\t)\Z/)
|
217
|
+
buffer = prepare_message(*T.unsafe([message, *T.unsafe(color)]))
|
218
|
+
buffer << "\n" if force_new_line && !message.to_s.end_with?("\n")
|
219
|
+
|
220
|
+
stderr.print(buffer)
|
221
|
+
stderr.flush
|
222
|
+
end
|
223
|
+
|
224
|
+
sig { params(eager_load: T::Boolean).void }
|
225
|
+
def load_application(eager_load:)
|
226
|
+
say("Loading Rails application... ")
|
227
|
+
|
228
|
+
loader.load_rails(
|
229
|
+
environment_load: true,
|
230
|
+
eager_load: eager_load
|
231
|
+
)
|
232
|
+
|
233
|
+
say("Done", :green)
|
234
|
+
end
|
235
|
+
|
236
|
+
sig { void }
|
237
|
+
def load_dsl_generators
|
238
|
+
say("Loading DSL generator classes... ")
|
239
|
+
|
240
|
+
Dir.glob([
|
241
|
+
"#{__dir__}/compilers/dsl/*.rb",
|
242
|
+
"#{Config::TAPIOCA_PATH}/generators/**/*.rb",
|
243
|
+
]).each do |generator|
|
244
|
+
require File.expand_path(generator)
|
245
|
+
end
|
246
|
+
|
247
|
+
say("Done", :green)
|
248
|
+
end
|
249
|
+
|
250
|
+
sig { params(constant_names: T::Array[String]).returns(T::Array[Module]) }
|
251
|
+
def constantize(constant_names)
|
252
|
+
constant_names.map do |name|
|
253
|
+
begin
|
254
|
+
name.constantize
|
255
|
+
rescue NameError
|
256
|
+
nil
|
257
|
+
end
|
258
|
+
end.compact
|
259
|
+
end
|
260
|
+
|
121
261
|
sig { returns(T::Hash[String, String]) }
|
122
262
|
def existing_rbis
|
123
263
|
@existing_rbis ||= Pathname.glob((config.outpath / "*@*.rbi").to_s)
|
124
|
-
.map { |f| f.basename(".*").to_s.split('@') }
|
264
|
+
.map { |f| T.cast(f.basename(".*").to_s.split('@', 2), [String, String]) }
|
125
265
|
.to_h
|
126
266
|
end
|
127
267
|
|
@@ -134,22 +274,22 @@ module Tapioca
|
|
134
274
|
end
|
135
275
|
|
136
276
|
sig { params(gem_name: String, version: String).returns(Pathname) }
|
137
|
-
def
|
277
|
+
def gem_rbi_filename(gem_name, version)
|
138
278
|
config.outpath / "#{gem_name}@#{version}.rbi"
|
139
279
|
end
|
140
280
|
|
141
281
|
sig { params(gem_name: String).returns(Pathname) }
|
142
282
|
def existing_rbi(gem_name)
|
143
|
-
|
283
|
+
gem_rbi_filename(gem_name, T.must(existing_rbis[gem_name]))
|
144
284
|
end
|
145
285
|
|
146
286
|
sig { params(gem_name: String).returns(Pathname) }
|
147
287
|
def expected_rbi(gem_name)
|
148
|
-
|
288
|
+
gem_rbi_filename(gem_name, T.must(expected_rbis[gem_name]))
|
149
289
|
end
|
150
290
|
|
151
291
|
sig { params(gem_name: String).returns(T::Boolean) }
|
152
|
-
def
|
292
|
+
def gem_rbi_exists?(gem_name)
|
153
293
|
existing_rbis.key?(gem_name)
|
154
294
|
end
|
155
295
|
|
@@ -227,13 +367,13 @@ module Tapioca
|
|
227
367
|
gems.each do |gem_name|
|
228
368
|
filename = expected_rbi(gem_name)
|
229
369
|
|
230
|
-
if
|
370
|
+
if gem_rbi_exists?(gem_name)
|
231
371
|
old_filename = existing_rbi(gem_name)
|
232
372
|
move(old_filename, filename) unless old_filename == filename
|
233
373
|
end
|
234
374
|
|
235
375
|
gem = T.must(bundle.gem(gem_name))
|
236
|
-
|
376
|
+
compile_gem_rbi(gem)
|
237
377
|
add(filename)
|
238
378
|
|
239
379
|
puts
|
@@ -265,37 +405,73 @@ module Tapioca
|
|
265
405
|
end
|
266
406
|
end
|
267
407
|
|
268
|
-
sig { params(command: String,
|
269
|
-
def rbi_header(command,
|
270
|
-
<<~HEAD
|
271
|
-
#
|
272
|
-
#
|
408
|
+
sig { params(command: String, reason: T.nilable(String), strictness: T.nilable(String)).returns(String) }
|
409
|
+
def rbi_header(command, reason: nil, strictness: nil)
|
410
|
+
statement = <<~HEAD
|
411
|
+
# DO NOT EDIT MANUALLY
|
412
|
+
# This is an autogenerated file for #{reason}.
|
413
|
+
# Please instead update this file by running `#{command}`.
|
414
|
+
HEAD
|
273
415
|
|
274
|
-
|
416
|
+
sigil = <<~SIGIL if strictness
|
417
|
+
# typed: #{strictness}
|
418
|
+
SIGIL
|
275
419
|
|
276
|
-
|
420
|
+
[statement, sigil].compact.join("\n").strip.concat("\n\n")
|
277
421
|
end
|
278
422
|
|
279
423
|
sig { params(gem: Gemfile::Gem).void }
|
280
|
-
def
|
424
|
+
def compile_gem_rbi(gem)
|
281
425
|
compiler = Compilers::SymbolTableCompiler.new
|
282
426
|
gem_name = set_color(gem.name, :yellow, :bold)
|
283
427
|
say("Compiling #{gem_name}, this may take a few seconds... ")
|
284
428
|
|
285
|
-
|
286
|
-
|
287
|
-
content =
|
288
|
-
content
|
429
|
+
strictness = config.typed_overrides[gem.name] || "true"
|
430
|
+
rbi_body_content = compiler.compile(gem)
|
431
|
+
content = String.new
|
432
|
+
content << rbi_header(
|
433
|
+
config.generate_command,
|
434
|
+
reason: "types exported from the `#{gem.name}` gem",
|
435
|
+
strictness: strictness
|
436
|
+
)
|
289
437
|
|
290
438
|
FileUtils.mkdir_p(config.outdir)
|
291
439
|
filename = config.outpath / gem.rbi_file_name
|
292
|
-
File.write(filename.to_s, content)
|
293
440
|
|
294
|
-
|
441
|
+
if rbi_body_content.strip.empty?
|
442
|
+
content << EMPTY_RBI_COMMENT
|
443
|
+
say("Done (empty output)", :yellow)
|
444
|
+
else
|
445
|
+
content << rbi_body_content
|
446
|
+
say("Done", :green)
|
447
|
+
end
|
448
|
+
File.write(filename.to_s, content)
|
295
449
|
|
296
450
|
Pathname.glob((config.outpath / "#{gem.name}@*.rbi").to_s) do |file|
|
297
451
|
remove(file) unless file.basename.to_s == gem.rbi_file_name
|
298
452
|
end
|
299
453
|
end
|
454
|
+
|
455
|
+
sig { params(constant: Module, contents: String).void }
|
456
|
+
def compile_dsl_rbi(constant, contents)
|
457
|
+
return if contents.nil?
|
458
|
+
|
459
|
+
command = format(config.generate_command, constant.name)
|
460
|
+
constant_name = Module.instance_method(:name).bind(constant).call
|
461
|
+
rbi_name = constant_name.underscore + ".rbi"
|
462
|
+
filename = config.outpath / rbi_name
|
463
|
+
|
464
|
+
out = String.new
|
465
|
+
out << rbi_header(
|
466
|
+
command,
|
467
|
+
reason: "dynamic methods in `#{constant.name}`"
|
468
|
+
)
|
469
|
+
out << contents
|
470
|
+
|
471
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
472
|
+
File.write(filename, out)
|
473
|
+
say("Wrote: ", [:green])
|
474
|
+
say(filename)
|
475
|
+
end
|
300
476
|
end
|
301
477
|
end
|