tapioca 0.16.4 → 0.16.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -4
- data/exe/tapioca +5 -0
- data/lib/ruby_lsp/tapioca/addon.rb +132 -0
- data/lib/ruby_lsp/tapioca/server_addon.rb +30 -0
- data/lib/tapioca/cli.rb +13 -2
- data/lib/tapioca/commands/abstract_dsl.rb +13 -2
- data/lib/tapioca/commands/abstract_gem.rb +4 -34
- data/lib/tapioca/commands/dsl_generate.rb +1 -1
- data/lib/tapioca/commands/gem_generate.rb +35 -0
- data/lib/tapioca/dsl/compiler.rb +11 -8
- data/lib/tapioca/dsl/compilers/active_model_attributes.rb +11 -11
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +62 -30
- data/lib/tapioca/dsl/compilers/json_api_client_resource.rb +10 -0
- data/lib/tapioca/dsl/helpers/active_model_type_helper.rb +12 -1
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +5 -0
- data/lib/tapioca/dsl/pipeline.rb +10 -2
- data/lib/tapioca/gemfile.rb +17 -8
- data/lib/tapioca/helpers/git_attributes.rb +2 -1
- data/lib/tapioca/helpers/source_uri.rb +10 -3
- data/lib/tapioca/loaders/dsl.rb +26 -4
- data/lib/tapioca/static/symbol_loader.rb +2 -0
- data/lib/tapioca/version.rb +1 -1
- metadata +5 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a42a478b0396f43dd9e6505c18ac69ca8c32e6ef00b298e6d6e4a8c12536b17
|
4
|
+
data.tar.gz: f24ab38ee1d78f1633f7d580ea5be8f93e13c9c6550d11b5d5bc4171d974929b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f623d3b5e66a081728d9ae4b3dde4c6c976829ffbd6e606a64def2e8aff489c09911396ca72146c901f72a11d432d6c86ae840e278b8d85fd8ee756e97776542
|
7
|
+
data.tar.gz: e7686bcfed28858bfb8f162ee0bd8a7c9a6cdf286c7227a86032025934b04fa72c29f21c41d7a3c5668edb8aad2d39bf2808a2d98799a7434e94aa9b921c3cf8
|
data/README.md
CHANGED
@@ -489,8 +489,7 @@ Options:
|
|
489
489
|
# Default: false
|
490
490
|
-q, [--quiet], [--no-quiet], [--skip-quiet] # Suppresses file creation output
|
491
491
|
# Default: false
|
492
|
-
-w, [--workers=N] # Number of parallel workers to use when generating RBIs (default:
|
493
|
-
# Default: 2
|
492
|
+
-w, [--workers=N] # Number of parallel workers to use when generating RBIs (default: auto)
|
494
493
|
[--rbi-max-line-length=N] # Set the max line length of generated RBIs. Signatures longer than the max line length will be wrapped
|
495
494
|
# Default: 120
|
496
495
|
-e, [--environment=ENVIRONMENT] # The Rack/Rails environment to use when generating RBIs
|
@@ -949,7 +948,7 @@ dsl:
|
|
949
948
|
exclude: []
|
950
949
|
verify: false
|
951
950
|
quiet: false
|
952
|
-
workers:
|
951
|
+
workers: 1
|
953
952
|
rbi_max_line_length: 120
|
954
953
|
environment: development
|
955
954
|
list_compilers: false
|
@@ -1000,7 +999,16 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/Shopif
|
|
1000
999
|
|
1001
1000
|
### DSL compilers
|
1002
1001
|
|
1003
|
-
|
1002
|
+
Tapioca ships with a small collection of high quality DSL compilers for popular Ruby gems that are used heavily at Shopify, like Rails and GraphQL. We encourage the community to contribute new DSL compilers, though they shouldn't necessarily live in the Tapioca repo itself.
|
1003
|
+
|
1004
|
+
It's best for DSL compilers to be contributed directly to gems they apply to ([example](https://github.com/Shopify/measured/tree/main/lib/tapioca/dsl/compilers)). This way, when changes are made to the gem's DSL, the gem's DSL compiler can be updated at the same time and be versioned/released together.
|
1005
|
+
|
1006
|
+
If an upstream gem's maintainers don't wish to host a DSL compiler themselves, you can propose contributing it to:
|
1007
|
+
|
1008
|
+
1. Tapioca, if it's a gem that Shopify uses (ask us in an issue or PR)
|
1009
|
+
2. A third party DSL compiler repository, like [AngelList/Boba](https://github.com/angellist/boba). These are not supported by Shopify.
|
1010
|
+
|
1011
|
+
For help writing a DSL compiler, please refer to [writing custom dsl compilers](https://github.com/Shopify/tapioca?tab=readme-ov-file#writing-custom-dsl-compilers).
|
1004
1012
|
|
1005
1013
|
## License
|
1006
1014
|
|
data/exe/tapioca
CHANGED
@@ -20,6 +20,11 @@ unless ENV["ENFORCE_TYPECHECKING"] == "1"
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
unless defined?(Bundler)
|
24
|
+
puts "Warning: You're running tapioca without Bundler. This isn't recommended and may cause issues. " \
|
25
|
+
"Please use the provided binstub through `bin/tapioca` instead."
|
26
|
+
end
|
27
|
+
|
23
28
|
require_relative "../lib/tapioca/internal"
|
24
29
|
|
25
30
|
Tapioca::Cli.start(ARGV)
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
RubyLsp::Addon.depend_on_ruby_lsp!(">= 0.22.1", "< 0.23")
|
5
|
+
|
6
|
+
begin
|
7
|
+
# The Tapioca add-on depends on the Rails add-on to add a runtime component to the runtime server. We can allow the
|
8
|
+
# add-on to work outside of a Rails context in the future, but that may require Tapioca spawning its own runtime
|
9
|
+
# server
|
10
|
+
require "ruby_lsp/ruby_lsp_rails/runner_client"
|
11
|
+
rescue LoadError
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
15
|
+
require "zlib"
|
16
|
+
|
17
|
+
module RubyLsp
|
18
|
+
module Tapioca
|
19
|
+
class Addon < ::RubyLsp::Addon
|
20
|
+
extend T::Sig
|
21
|
+
|
22
|
+
sig { void }
|
23
|
+
def initialize
|
24
|
+
super
|
25
|
+
|
26
|
+
@global_state = T.let(nil, T.nilable(RubyLsp::GlobalState))
|
27
|
+
@rails_runner_client = T.let(nil, T.nilable(RubyLsp::Rails::RunnerClient))
|
28
|
+
@index = T.let(nil, T.nilable(RubyIndexer::Index))
|
29
|
+
@file_checksums = T.let({}, T::Hash[String, String])
|
30
|
+
@outgoing_queue = T.let(nil, T.nilable(Thread::Queue))
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { override.params(global_state: RubyLsp::GlobalState, outgoing_queue: Thread::Queue).void }
|
34
|
+
def activate(global_state, outgoing_queue)
|
35
|
+
@global_state = global_state
|
36
|
+
return unless @global_state.enabled_feature?(:tapiocaAddon)
|
37
|
+
|
38
|
+
@index = @global_state.index
|
39
|
+
@outgoing_queue = outgoing_queue
|
40
|
+
Thread.new do
|
41
|
+
# Get a handle to the Rails add-on's runtime client. The call to `rails_runner_client` will block this thread
|
42
|
+
# until the server has finished booting, but it will not block the main LSP. This has to happen inside of a
|
43
|
+
# thread
|
44
|
+
addon = T.cast(::RubyLsp::Addon.get("Ruby LSP Rails", ">= 0.3.17", "< 0.4"), ::RubyLsp::Rails::Addon)
|
45
|
+
@rails_runner_client = addon.rails_runner_client
|
46
|
+
@outgoing_queue << Notification.window_log_message("Activating Tapioca add-on v#{version}")
|
47
|
+
@rails_runner_client.register_server_addon(File.expand_path("server_addon.rb", __dir__))
|
48
|
+
rescue IncompatibleApiError
|
49
|
+
# The requested version for the Rails add-on no longer matches. We need to upgrade and fix the breaking
|
50
|
+
# changes
|
51
|
+
@outgoing_queue << Notification.window_log_message(
|
52
|
+
"IncompatibleApiError: Cannot activate Tapioca LSP add-on",
|
53
|
+
type: Constant::MessageType::WARNING,
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { override.void }
|
59
|
+
def deactivate
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { override.returns(String) }
|
63
|
+
def name
|
64
|
+
"Tapioca"
|
65
|
+
end
|
66
|
+
|
67
|
+
sig { override.returns(String) }
|
68
|
+
def version
|
69
|
+
"0.1.0"
|
70
|
+
end
|
71
|
+
|
72
|
+
sig { params(changes: T::Array[{ uri: String, type: Integer }]).void }
|
73
|
+
def workspace_did_change_watched_files(changes)
|
74
|
+
return unless T.must(@global_state).enabled_feature?(:tapiocaAddon)
|
75
|
+
return unless @rails_runner_client # Client is not ready
|
76
|
+
|
77
|
+
constants = changes.flat_map do |change|
|
78
|
+
path = URI(change[:uri]).to_standardized_path
|
79
|
+
next if path.end_with?("_test.rb", "_spec.rb")
|
80
|
+
next unless file_updated?(change, path)
|
81
|
+
|
82
|
+
entries = T.must(@index).entries_for(path)
|
83
|
+
next unless entries
|
84
|
+
|
85
|
+
entries.filter_map do |entry|
|
86
|
+
entry.name if entry.class == RubyIndexer::Entry::Class || entry.class == RubyIndexer::Entry::Module
|
87
|
+
end
|
88
|
+
end.compact
|
89
|
+
|
90
|
+
return if constants.empty?
|
91
|
+
|
92
|
+
@rails_runner_client.trigger_reload
|
93
|
+
@rails_runner_client.delegate_notification(
|
94
|
+
server_addon_name: "Tapioca",
|
95
|
+
request_name: "dsl",
|
96
|
+
constants: constants,
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
sig { params(change: T::Hash[Symbol, T.untyped], path: String).returns(T::Boolean) }
|
103
|
+
def file_updated?(change, path)
|
104
|
+
case change[:type]
|
105
|
+
when Constant::FileChangeType::CREATED
|
106
|
+
@file_checksums[path] = Zlib.crc32(File.read(path)).to_s
|
107
|
+
return true
|
108
|
+
when Constant::FileChangeType::CHANGED
|
109
|
+
current_checksum = Zlib.crc32(File.read(path)).to_s
|
110
|
+
if @file_checksums[path] == current_checksum
|
111
|
+
T.must(@outgoing_queue) << Notification.window_log_message(
|
112
|
+
"File has not changed. Skipping #{path}",
|
113
|
+
type: Constant::MessageType::INFO,
|
114
|
+
)
|
115
|
+
else
|
116
|
+
@file_checksums[path] = current_checksum
|
117
|
+
return true
|
118
|
+
end
|
119
|
+
when Constant::FileChangeType::DELETED
|
120
|
+
@file_checksums.delete(path)
|
121
|
+
else
|
122
|
+
T.must(@outgoing_queue) << Notification.window_log_message(
|
123
|
+
"Unexpected file change type: #{change[:type]}",
|
124
|
+
type: Constant::MessageType::WARNING,
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "tapioca/internal"
|
5
|
+
|
6
|
+
module RubyLsp
|
7
|
+
module Tapioca
|
8
|
+
class ServerAddon < ::RubyLsp::Rails::ServerAddon
|
9
|
+
def name
|
10
|
+
"Tapioca"
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(request, params)
|
14
|
+
case request
|
15
|
+
when "dsl"
|
16
|
+
fork do
|
17
|
+
dsl(params)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def dsl(params)
|
25
|
+
load("tapioca/cli.rb") # Reload the CLI to reset thor defaults between requests
|
26
|
+
::Tapioca::Cli.start(["dsl", "--lsp_addon", "--workers=1"] + params[:constants])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/tapioca/cli.rb
CHANGED
@@ -111,8 +111,7 @@ module Tapioca
|
|
111
111
|
option :workers,
|
112
112
|
aliases: ["-w"],
|
113
113
|
type: :numeric,
|
114
|
-
desc: "Number of parallel workers to use when generating RBIs (default:
|
115
|
-
default: 2
|
114
|
+
desc: "Number of parallel workers to use when generating RBIs (default: auto)"
|
116
115
|
option :rbi_max_line_length,
|
117
116
|
type: :numeric,
|
118
117
|
desc: "Set the max line length of generated RBIs. Signatures longer than the max line length will be wrapped",
|
@@ -144,6 +143,11 @@ module Tapioca
|
|
144
143
|
type: :hash,
|
145
144
|
desc: "Options to pass to the DSL compilers",
|
146
145
|
default: {}
|
146
|
+
option :lsp_addon,
|
147
|
+
type: :boolean,
|
148
|
+
desc: "Generate DSL RBIs from the LSP add-on. Internal to Tapioca and not intended for end-users",
|
149
|
+
default: false,
|
150
|
+
hide: true
|
147
151
|
def dsl(*constant_or_paths)
|
148
152
|
set_environment(options)
|
149
153
|
|
@@ -166,6 +170,7 @@ module Tapioca
|
|
166
170
|
app_root: options[:app_root],
|
167
171
|
halt_upon_load_error: options[:halt_upon_load_error],
|
168
172
|
compiler_options: options[:compiler_options],
|
173
|
+
lsp_addon: options[:lsp_addon],
|
169
174
|
}
|
170
175
|
|
171
176
|
command = if options[:verify]
|
@@ -261,6 +266,11 @@ module Tapioca
|
|
261
266
|
type: :boolean,
|
262
267
|
desc: "Halt upon a load error while loading the Rails application",
|
263
268
|
default: true
|
269
|
+
option :lsp_addon,
|
270
|
+
type: :boolean,
|
271
|
+
desc: "Generate Gem RBIs from the LSP add-on. Internal to Tapioca and not intended for end-users",
|
272
|
+
default: false,
|
273
|
+
hide: true
|
264
274
|
def gem(*gems)
|
265
275
|
set_environment(options)
|
266
276
|
|
@@ -295,6 +305,7 @@ module Tapioca
|
|
295
305
|
dsl_dir: options[:dsl_dir],
|
296
306
|
rbi_formatter: rbi_formatter(options),
|
297
307
|
halt_upon_load_error: options[:halt_upon_load_error],
|
308
|
+
lsp_addon: options[:lsp_addon],
|
298
309
|
}
|
299
310
|
|
300
311
|
command = if verify
|
@@ -28,6 +28,7 @@ module Tapioca
|
|
28
28
|
app_root: String,
|
29
29
|
halt_upon_load_error: T::Boolean,
|
30
30
|
compiler_options: T::Hash[String, T.untyped],
|
31
|
+
lsp_addon: T::Boolean,
|
31
32
|
).void
|
32
33
|
end
|
33
34
|
def initialize(
|
@@ -47,7 +48,8 @@ module Tapioca
|
|
47
48
|
rbi_formatter: DEFAULT_RBI_FORMATTER,
|
48
49
|
app_root: ".",
|
49
50
|
halt_upon_load_error: true,
|
50
|
-
compiler_options: {}
|
51
|
+
compiler_options: {},
|
52
|
+
lsp_addon: false
|
51
53
|
)
|
52
54
|
@requested_constants = requested_constants
|
53
55
|
@requested_paths = requested_paths
|
@@ -66,6 +68,7 @@ module Tapioca
|
|
66
68
|
@halt_upon_load_error = halt_upon_load_error
|
67
69
|
@skip_constant = skip_constant
|
68
70
|
@compiler_options = compiler_options
|
71
|
+
@lsp_addon = lsp_addon
|
69
72
|
|
70
73
|
super()
|
71
74
|
end
|
@@ -74,6 +77,10 @@ module Tapioca
|
|
74
77
|
|
75
78
|
sig { params(outpath: Pathname, quiet: T::Boolean).returns(T::Set[Pathname]) }
|
76
79
|
def generate_dsl_rbi_files(outpath, quiet:)
|
80
|
+
if @lsp_addon
|
81
|
+
pipeline.active_compilers.each(&:reset_state)
|
82
|
+
end
|
83
|
+
|
77
84
|
existing_rbi_files = existing_rbi_filenames(all_requested_constants)
|
78
85
|
|
79
86
|
generated_files = pipeline.run do |constant, contents|
|
@@ -116,6 +123,7 @@ module Tapioca
|
|
116
123
|
eager_load: @requested_constants.empty? && @requested_paths.empty?,
|
117
124
|
app_root: @app_root,
|
118
125
|
halt_upon_load_error: @halt_upon_load_error,
|
126
|
+
lsp_addon: @lsp_addon,
|
119
127
|
)
|
120
128
|
end
|
121
129
|
|
@@ -133,6 +141,7 @@ module Tapioca
|
|
133
141
|
skipped_constants: constantize(@skip_constant, ignore_missing: true),
|
134
142
|
number_of_workers: @number_of_workers,
|
135
143
|
compiler_options: @compiler_options,
|
144
|
+
lsp_addon: @lsp_addon,
|
136
145
|
)
|
137
146
|
end
|
138
147
|
|
@@ -170,7 +179,9 @@ module Tapioca
|
|
170
179
|
raise Thor::Error, ""
|
171
180
|
end
|
172
181
|
|
173
|
-
processable_constants
|
182
|
+
processable_constants
|
183
|
+
.map { |_, constant| constant }
|
184
|
+
.grep(Module)
|
174
185
|
end
|
175
186
|
|
176
187
|
sig { params(compiler_names: T::Array[String]).returns(T::Array[T.class_of(Tapioca::Dsl::Compiler)]) }
|
@@ -27,6 +27,7 @@ module Tapioca
|
|
27
27
|
dsl_dir: String,
|
28
28
|
rbi_formatter: RBIFormatter,
|
29
29
|
halt_upon_load_error: T::Boolean,
|
30
|
+
lsp_addon: T.nilable(T::Boolean),
|
30
31
|
).void
|
31
32
|
end
|
32
33
|
def initialize(
|
@@ -45,7 +46,8 @@ module Tapioca
|
|
45
46
|
auto_strictness: true,
|
46
47
|
dsl_dir: DEFAULT_DSL_DIR,
|
47
48
|
rbi_formatter: DEFAULT_RBI_FORMATTER,
|
48
|
-
halt_upon_load_error: true
|
49
|
+
halt_upon_load_error: true,
|
50
|
+
lsp_addon: false
|
49
51
|
)
|
50
52
|
@gem_names = gem_names
|
51
53
|
@exclude = exclude
|
@@ -59,6 +61,7 @@ module Tapioca
|
|
59
61
|
@auto_strictness = auto_strictness
|
60
62
|
@dsl_dir = dsl_dir
|
61
63
|
@rbi_formatter = rbi_formatter
|
64
|
+
@lsp_addon = lsp_addon
|
62
65
|
|
63
66
|
super()
|
64
67
|
|
@@ -73,39 +76,6 @@ module Tapioca
|
|
73
76
|
|
74
77
|
private
|
75
78
|
|
76
|
-
sig { params(gem_names: T::Array[String]).returns(T::Array[Gemfile::GemSpec]) }
|
77
|
-
def gems_to_generate(gem_names)
|
78
|
-
return @bundle.dependencies if gem_names.empty?
|
79
|
-
|
80
|
-
gem_names.each_with_object([]) do |gem_name, gems|
|
81
|
-
gem = @bundle.gem(gem_name)
|
82
|
-
|
83
|
-
if gem.nil?
|
84
|
-
raise Thor::Error, set_color("Error: Cannot find gem '#{gem_name}'", :red)
|
85
|
-
end
|
86
|
-
|
87
|
-
gems.concat(gem_dependencies(gem)) if @include_dependencies
|
88
|
-
gems << gem
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
sig do
|
93
|
-
params(
|
94
|
-
gem: Gemfile::GemSpec,
|
95
|
-
dependencies: T::Array[Gemfile::GemSpec],
|
96
|
-
).returns(T::Array[Gemfile::GemSpec])
|
97
|
-
end
|
98
|
-
def gem_dependencies(gem, dependencies = [])
|
99
|
-
direct_dependencies = gem.dependencies.filter_map { |dependency| @bundle.gem(dependency.name) }
|
100
|
-
gems = dependencies | direct_dependencies
|
101
|
-
|
102
|
-
if direct_dependencies.empty?
|
103
|
-
gems
|
104
|
-
else
|
105
|
-
direct_dependencies.reduce(gems) { |result, gem| gem_dependencies(gem, result) }
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
79
|
sig { params(gem: Gemfile::GemSpec).void }
|
110
80
|
def compile_gem_rbi(gem)
|
111
81
|
gem_name = set_color(gem.name, :yellow, :bold)
|
@@ -46,6 +46,41 @@ module Tapioca
|
|
46
46
|
ensure
|
47
47
|
GitAttributes.create_generated_attribute_file(@outpath)
|
48
48
|
end
|
49
|
+
|
50
|
+
sig { params(gem_names: T::Array[String]).returns(T::Array[Gemfile::GemSpec]) }
|
51
|
+
def gems_to_generate(gem_names)
|
52
|
+
return @bundle.dependencies if gem_names.empty?
|
53
|
+
|
54
|
+
(gem_names - @exclude).each_with_object([]) do |gem_name, gems|
|
55
|
+
gem = @bundle.gem(gem_name)
|
56
|
+
|
57
|
+
if gem.nil?
|
58
|
+
next if @lsp_addon
|
59
|
+
|
60
|
+
raise Thor::Error, set_color("Error: Cannot find gem '#{gem_name}'", :red)
|
61
|
+
end
|
62
|
+
|
63
|
+
gems.concat(gem_dependencies(gem)) if @include_dependencies
|
64
|
+
gems << gem
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
sig do
|
69
|
+
params(
|
70
|
+
gem: Gemfile::GemSpec,
|
71
|
+
dependencies: T::Array[Gemfile::GemSpec],
|
72
|
+
).returns(T::Array[Gemfile::GemSpec])
|
73
|
+
end
|
74
|
+
def gem_dependencies(gem, dependencies = [])
|
75
|
+
direct_dependencies = gem.dependencies.filter_map { |dependency| @bundle.gem(dependency.name) }
|
76
|
+
gems = dependencies | direct_dependencies
|
77
|
+
|
78
|
+
if direct_dependencies.empty?
|
79
|
+
gems
|
80
|
+
else
|
81
|
+
direct_dependencies.reduce(gems) { |result, gem| gem_dependencies(gem, result) }
|
82
|
+
end
|
83
|
+
end
|
49
84
|
end
|
50
85
|
end
|
51
86
|
end
|
data/lib/tapioca/dsl/compiler.rb
CHANGED
@@ -51,6 +51,13 @@ module Tapioca
|
|
51
51
|
@@requested_constants = constants # rubocop:disable Style/ClassVars
|
52
52
|
end
|
53
53
|
|
54
|
+
sig { void }
|
55
|
+
def reset_state
|
56
|
+
@processable_constants = nil
|
57
|
+
@all_classes = nil
|
58
|
+
@all_modules = nil
|
59
|
+
end
|
60
|
+
|
54
61
|
private
|
55
62
|
|
56
63
|
sig do
|
@@ -74,11 +81,7 @@ module Tapioca
|
|
74
81
|
sig { returns(T::Enumerable[T::Class[T.anything]]) }
|
75
82
|
def all_classes
|
76
83
|
@all_classes ||= T.let(
|
77
|
-
|
78
|
-
@@requested_constants.grep(Class)
|
79
|
-
else
|
80
|
-
ObjectSpace.each_object(Class)
|
81
|
-
end,
|
84
|
+
all_modules.grep(Class).freeze,
|
82
85
|
T.nilable(T::Enumerable[T::Class[T.anything]]),
|
83
86
|
)
|
84
87
|
end
|
@@ -87,10 +90,10 @@ module Tapioca
|
|
87
90
|
def all_modules
|
88
91
|
@all_modules ||= T.let(
|
89
92
|
if @@requested_constants.any?
|
90
|
-
@@requested_constants.
|
93
|
+
@@requested_constants.grep(Module)
|
91
94
|
else
|
92
|
-
ObjectSpace.each_object(Module)
|
93
|
-
end,
|
95
|
+
ObjectSpace.each_object(Module).to_a
|
96
|
+
end.freeze,
|
94
97
|
T.nilable(T::Enumerable[Module]),
|
95
98
|
)
|
96
99
|
end
|
@@ -101,26 +101,26 @@ module Tapioca
|
|
101
101
|
|
102
102
|
sig { params(attribute_type_value: T.untyped).returns(::String) }
|
103
103
|
def type_for(attribute_type_value)
|
104
|
-
|
104
|
+
case attribute_type_value
|
105
105
|
when ActiveModel::Type::Boolean
|
106
|
-
"T::Boolean"
|
106
|
+
as_nilable_type("T::Boolean")
|
107
107
|
when ActiveModel::Type::Date
|
108
|
-
"::Date"
|
108
|
+
as_nilable_type("::Date")
|
109
109
|
when ActiveModel::Type::DateTime, ActiveModel::Type::Time
|
110
|
-
"::Time"
|
110
|
+
as_nilable_type("::Time")
|
111
111
|
when ActiveModel::Type::Decimal
|
112
|
-
"::BigDecimal"
|
112
|
+
as_nilable_type("::BigDecimal")
|
113
113
|
when ActiveModel::Type::Float
|
114
|
-
"::Float"
|
114
|
+
as_nilable_type("::Float")
|
115
115
|
when ActiveModel::Type::Integer
|
116
|
-
"::Integer"
|
116
|
+
as_nilable_type("::Integer")
|
117
117
|
when ActiveModel::Type::String
|
118
|
-
"::String"
|
118
|
+
as_nilable_type("::String")
|
119
119
|
else
|
120
|
-
Helpers::ActiveModelTypeHelper.type_for(attribute_type_value)
|
120
|
+
type = Helpers::ActiveModelTypeHelper.type_for(attribute_type_value)
|
121
|
+
type = as_nilable_type(type) if Helpers::ActiveModelTypeHelper.assume_nilable?(attribute_type_value)
|
122
|
+
type
|
121
123
|
end
|
122
|
-
|
123
|
-
as_nilable_type(type)
|
124
124
|
end
|
125
125
|
|
126
126
|
sig { params(klass: RBI::Scope, method: String, type: String).void }
|
@@ -210,10 +210,6 @@ module Tapioca
|
|
210
210
|
query_methods |= ActiveRecord::SpawnMethods.instance_methods(false)
|
211
211
|
# Remove the ones we know are private API
|
212
212
|
query_methods -= [:all, :arel, :build_subquery, :construct_join_dependency, :extensions, :spawn]
|
213
|
-
# Remove "group" which needs a custom return type for GroupChains
|
214
|
-
query_methods -= [:group]
|
215
|
-
# Remove "where" which needs a custom return type for WhereChains
|
216
|
-
query_methods -= [:where]
|
217
213
|
# Remove the methods that ...
|
218
214
|
query_methods
|
219
215
|
.grep_v(/_clause$/) # end with "_clause"
|
@@ -376,7 +372,7 @@ module Tapioca
|
|
376
372
|
return_type: "T.self_type",
|
377
373
|
)
|
378
374
|
|
379
|
-
CALCULATION_METHODS.each do |method_name|
|
375
|
+
(CALCULATION_METHODS + [:size]).each do |method_name|
|
380
376
|
case method_name
|
381
377
|
when :average, :maximum, :minimum
|
382
378
|
klass.create_method(
|
@@ -404,9 +400,9 @@ module Tapioca
|
|
404
400
|
],
|
405
401
|
return_type: "T::Hash[T.untyped, Integer]",
|
406
402
|
)
|
407
|
-
when :sum
|
403
|
+
when :sum, :size
|
408
404
|
klass.create_method(
|
409
|
-
|
405
|
+
method_name.to_s,
|
410
406
|
parameters: [
|
411
407
|
create_opt_param("column_name", type: "T.nilable(T.any(String, Symbol))", default: "nil"),
|
412
408
|
create_block_param("block", type: "T.nilable(T.proc.params(record: T.untyped).returns(T.untyped))"),
|
@@ -419,7 +415,7 @@ module Tapioca
|
|
419
415
|
|
420
416
|
sig { void }
|
421
417
|
def create_relation_where_chain_class
|
422
|
-
model.create_class(RelationWhereChainClassName
|
418
|
+
model.create_class(RelationWhereChainClassName) do |klass|
|
423
419
|
create_where_chain_methods(klass, RelationClassName)
|
424
420
|
klass.create_type_variable("Elem", type: "type_member", fixed: constant_name)
|
425
421
|
end
|
@@ -427,10 +423,7 @@ module Tapioca
|
|
427
423
|
|
428
424
|
sig { void }
|
429
425
|
def create_association_relation_where_chain_class
|
430
|
-
model.create_class(
|
431
|
-
AssociationRelationWhereChainClassName,
|
432
|
-
superclass_name: AssociationRelationClassName,
|
433
|
-
) do |klass|
|
426
|
+
model.create_class(AssociationRelationWhereChainClassName) do |klass|
|
434
427
|
create_where_chain_methods(klass, AssociationRelationClassName)
|
435
428
|
klass.create_type_variable("Elem", type: "type_member", fixed: constant_name)
|
436
429
|
end
|
@@ -560,27 +553,21 @@ module Tapioca
|
|
560
553
|
sig { void }
|
561
554
|
def create_relation_methods
|
562
555
|
create_relation_method("all")
|
563
|
-
create_relation_method(
|
564
|
-
"group",
|
565
|
-
parameters: [
|
566
|
-
create_rest_param("args", type: "T.untyped"),
|
567
|
-
create_block_param("blk", type: "T.untyped"),
|
568
|
-
],
|
569
|
-
relation_return_type: RelationGroupChainClassName,
|
570
|
-
association_return_type: AssociationRelationGroupChainClassName,
|
571
|
-
)
|
572
|
-
create_relation_method(
|
573
|
-
"where",
|
574
|
-
parameters: [
|
575
|
-
create_rest_param("args", type: "T.untyped"),
|
576
|
-
create_block_param("blk", type: "T.untyped"),
|
577
|
-
],
|
578
|
-
relation_return_type: RelationWhereChainClassName,
|
579
|
-
association_return_type: AssociationRelationWhereChainClassName,
|
580
|
-
)
|
581
556
|
|
582
557
|
QUERY_METHODS.each do |method_name|
|
583
558
|
case method_name
|
559
|
+
when :where
|
560
|
+
create_where_relation_method
|
561
|
+
when :group
|
562
|
+
create_relation_method(
|
563
|
+
"group",
|
564
|
+
parameters: [
|
565
|
+
create_rest_param("args", type: "T.untyped"),
|
566
|
+
create_block_param("blk", type: "T.untyped"),
|
567
|
+
],
|
568
|
+
relation_return_type: RelationGroupChainClassName,
|
569
|
+
association_return_type: AssociationRelationGroupChainClassName,
|
570
|
+
)
|
584
571
|
when :distinct
|
585
572
|
create_relation_method(
|
586
573
|
method_name.to_s,
|
@@ -599,6 +586,22 @@ module Tapioca
|
|
599
586
|
parameters: parameters,
|
600
587
|
return_type: return_type,
|
601
588
|
)
|
589
|
+
when :select
|
590
|
+
[relation_methods_module, association_relation_methods_module].each do |mod|
|
591
|
+
mod.create_method(method_name.to_s) do |method|
|
592
|
+
method.add_rest_param("args")
|
593
|
+
method.add_block_param("blk")
|
594
|
+
|
595
|
+
method.add_sig do |sig|
|
596
|
+
sig.add_param("args", "T.untyped")
|
597
|
+
sig.return_type = mod == relation_methods_module ? RelationClassName : AssociationRelationClassName
|
598
|
+
end
|
599
|
+
method.add_sig do |sig|
|
600
|
+
sig.add_param("blk", "T.proc.params(record: #{constant_name}).returns(T::Boolean)")
|
601
|
+
sig.return_type = "T::Array[#{constant_name}]"
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
602
605
|
else
|
603
606
|
create_relation_method(
|
604
607
|
method_name,
|
@@ -1056,6 +1059,35 @@ module Tapioca
|
|
1056
1059
|
)
|
1057
1060
|
end
|
1058
1061
|
|
1062
|
+
sig { void }
|
1063
|
+
def create_where_relation_method
|
1064
|
+
relation_methods_module.create_method("where") do |method|
|
1065
|
+
method.add_rest_param("args")
|
1066
|
+
|
1067
|
+
method.add_sig do |sig|
|
1068
|
+
sig.return_type = RelationWhereChainClassName
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
method.add_sig do |sig|
|
1072
|
+
sig.add_param("args", "T.untyped")
|
1073
|
+
sig.return_type = RelationClassName
|
1074
|
+
end
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
association_relation_methods_module.create_method("where") do |method|
|
1078
|
+
method.add_rest_param("args")
|
1079
|
+
|
1080
|
+
method.add_sig do |sig|
|
1081
|
+
sig.return_type = AssociationRelationWhereChainClassName
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
method.add_sig do |sig|
|
1085
|
+
sig.add_param("args", "T.untyped")
|
1086
|
+
sig.return_type = AssociationRelationClassName
|
1087
|
+
end
|
1088
|
+
end
|
1089
|
+
end
|
1090
|
+
|
1059
1091
|
sig do
|
1060
1092
|
params(
|
1061
1093
|
name: T.any(Symbol, String),
|
@@ -147,7 +147,17 @@ module Tapioca
|
|
147
147
|
return "T.untyped" if type.nil?
|
148
148
|
|
149
149
|
sorbet_type = if type.respond_to?(:sorbet_type)
|
150
|
+
line, file = type.method(:sorbet_type).source_location
|
151
|
+
|
152
|
+
$stderr.puts <<~MESSAGE
|
153
|
+
WARNING: `#sorbet_type` is deprecated. Please rename your method to `#__tapioca_type`."
|
154
|
+
|
155
|
+
Defined on line #{line} of #{file}
|
156
|
+
MESSAGE
|
157
|
+
|
150
158
|
type.sorbet_type
|
159
|
+
elsif type.respond_to?(:__tapioca_type)
|
160
|
+
type.__tapioca_type
|
151
161
|
elsif type == ::JsonApiClient::Schema::Types::Integer
|
152
162
|
"::Integer"
|
153
163
|
elsif type == ::JsonApiClient::Schema::Types::String
|
@@ -14,7 +14,8 @@ module Tapioca
|
|
14
14
|
def type_for(type_value)
|
15
15
|
return "T.untyped" if Runtime::GenericTypeRegistry.generic_type_instance?(type_value)
|
16
16
|
|
17
|
-
type =
|
17
|
+
type = lookup_tapioca_type(type_value) ||
|
18
|
+
lookup_return_type_of_method(type_value, :deserialize) ||
|
18
19
|
lookup_return_type_of_method(type_value, :cast) ||
|
19
20
|
lookup_return_type_of_method(type_value, :cast_value) ||
|
20
21
|
lookup_arg_type_of_method(type_value, :serialize) ||
|
@@ -22,6 +23,11 @@ module Tapioca
|
|
22
23
|
type.to_s
|
23
24
|
end
|
24
25
|
|
26
|
+
sig { params(type_value: T.untyped).returns(T::Boolean) }
|
27
|
+
def assume_nilable?(type_value)
|
28
|
+
!type_value.respond_to?(:__tapioca_type)
|
29
|
+
end
|
30
|
+
|
25
31
|
private
|
26
32
|
|
27
33
|
MEANINGLESS_TYPES = T.let(
|
@@ -39,6 +45,11 @@ module Tapioca
|
|
39
45
|
!MEANINGLESS_TYPES.include?(type)
|
40
46
|
end
|
41
47
|
|
48
|
+
sig { params(obj: T.untyped).returns(T.nilable(T::Types::Base)) }
|
49
|
+
def lookup_tapioca_type(obj)
|
50
|
+
T::Utils.coerce(obj.__tapioca_type) if obj.respond_to?(:__tapioca_type)
|
51
|
+
end
|
52
|
+
|
42
53
|
sig { params(obj: T.untyped, method: Symbol).returns(T.nilable(T::Types::Base)) }
|
43
54
|
def lookup_return_type_of_method(obj, method)
|
44
55
|
return_type = lookup_signature_of_method(obj, method)&.return_type
|
@@ -211,6 +211,11 @@ module Tapioca
|
|
211
211
|
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range === type
|
212
212
|
}
|
213
213
|
"T::Range[#{type_for_activerecord_value(column_type.subtype, column_nullability:)}]"
|
214
|
+
when ->(type) {
|
215
|
+
defined?(ActiveRecord::Locking::LockingType) &&
|
216
|
+
ActiveRecord::Locking::LockingType === type
|
217
|
+
}
|
218
|
+
as_non_nilable_if_persisted_and_not_nullable("::Integer", column_nullability:)
|
214
219
|
else
|
215
220
|
as_non_nilable_if_persisted_and_not_nullable(
|
216
221
|
ActiveModelTypeHelper.type_for(column_type),
|
data/lib/tapioca/dsl/pipeline.rb
CHANGED
@@ -34,6 +34,7 @@ module Tapioca
|
|
34
34
|
skipped_constants: T::Array[Module],
|
35
35
|
number_of_workers: T.nilable(Integer),
|
36
36
|
compiler_options: T::Hash[String, T.untyped],
|
37
|
+
lsp_addon: T::Boolean,
|
37
38
|
).void
|
38
39
|
end
|
39
40
|
def initialize(
|
@@ -44,7 +45,8 @@ module Tapioca
|
|
44
45
|
error_handler: $stderr.method(:puts).to_proc,
|
45
46
|
skipped_constants: [],
|
46
47
|
number_of_workers: nil,
|
47
|
-
compiler_options: {}
|
48
|
+
compiler_options: {},
|
49
|
+
lsp_addon: false
|
48
50
|
)
|
49
51
|
@active_compilers = T.let(
|
50
52
|
gather_active_compilers(requested_compilers, excluded_compilers),
|
@@ -56,6 +58,7 @@ module Tapioca
|
|
56
58
|
@skipped_constants = skipped_constants
|
57
59
|
@number_of_workers = number_of_workers
|
58
60
|
@compiler_options = compiler_options
|
61
|
+
@lsp_addon = lsp_addon
|
59
62
|
@errors = T.let([], T::Array[String])
|
60
63
|
end
|
61
64
|
|
@@ -177,7 +180,7 @@ module Tapioca
|
|
177
180
|
# Find the constants that have been reloaded
|
178
181
|
reloaded_constants = constants_by_name.select { |_, constants| constants.size > 1 }.keys
|
179
182
|
|
180
|
-
unless reloaded_constants.empty?
|
183
|
+
unless reloaded_constants.empty? || @lsp_addon
|
181
184
|
reloaded_constant_names = reloaded_constants.map { |name| "`#{name}`" }.join(", ")
|
182
185
|
|
183
186
|
$stderr.puts("WARNING: Multiple constants with the same name: #{reloaded_constant_names}")
|
@@ -222,9 +225,14 @@ module Tapioca
|
|
222
225
|
|
223
226
|
sig { void }
|
224
227
|
def abort_if_pending_migrations!
|
228
|
+
# When running within the add-on, we cannot invoke the abort if pending migrations task because that will exit
|
229
|
+
# the process and crash the Rails runtime server. Instead, the Rails add-on checks for pending migrations and
|
230
|
+
# warns the user, so that they are aware they need to migrate their database
|
231
|
+
return if @lsp_addon
|
225
232
|
return unless defined?(::Rake)
|
226
233
|
|
227
234
|
Rails.application.load_tasks
|
235
|
+
|
228
236
|
if Rake::Task.task_defined?("db:abort_if_pending_migrations")
|
229
237
|
Rake::Task["db:abort_if_pending_migrations"].invoke
|
230
238
|
end
|
data/lib/tapioca/gemfile.rb
CHANGED
@@ -66,15 +66,24 @@ module Tapioca
|
|
66
66
|
|
67
67
|
sig { returns([T::Enumerable[Spec], T::Array[String]]) }
|
68
68
|
def materialize_deps
|
69
|
-
deps = definition.locked_gems.dependencies.values
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
69
|
+
deps = definition.locked_gems.dependencies.except(*@excluded_gems).values
|
70
|
+
resolve = definition.resolve
|
71
|
+
materialized_dependencies = resolve.materialize(deps)
|
72
|
+
|
73
|
+
if Bundler::VERSION >= "2.6.0"
|
74
|
+
missing_specs = resolve.missing_specs.map do |spec|
|
75
|
+
"#{spec.name} (#{spec.version})"
|
76
|
+
end
|
77
|
+
else
|
78
|
+
missing_spec_names = materialized_dependencies.missing_specs.map(&:name).to_set
|
79
|
+
missing_specs = materialized_dependencies.missing_specs.map do |spec|
|
80
|
+
"#{spec.name} (#{spec.version})"
|
81
|
+
end
|
82
|
+
materialized_dependencies = materialized_dependencies.to_a.reject do |spec|
|
83
|
+
missing_spec_names.include?(spec.name)
|
84
|
+
end
|
77
85
|
end
|
86
|
+
|
78
87
|
[materialized_dependencies, missing_specs]
|
79
88
|
end
|
80
89
|
|
@@ -28,7 +28,8 @@ class GitAttributes
|
|
28
28
|
# exist, we just return.
|
29
29
|
return unless path.exist?
|
30
30
|
|
31
|
-
|
31
|
+
git_attributes_path = path.join(".gitattributes")
|
32
|
+
File.write(git_attributes_path, content) unless git_attributes_path.exist?
|
32
33
|
end
|
33
34
|
end
|
34
35
|
end
|
@@ -25,9 +25,6 @@ module URI
|
|
25
25
|
# have the uri gem in their own bundle and thus not use a compatible version.
|
26
26
|
PARSER = T.let(const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER, RFC2396_Parser)
|
27
27
|
|
28
|
-
alias_method(:gem_name, :host)
|
29
|
-
alias_method(:line_number, :fragment)
|
30
|
-
|
31
28
|
sig { returns(T.nilable(String)) }
|
32
29
|
attr_reader :gem_version
|
33
30
|
|
@@ -54,6 +51,16 @@ module URI
|
|
54
51
|
end
|
55
52
|
end
|
56
53
|
|
54
|
+
sig { returns(T.nilable(String)) }
|
55
|
+
def gem_name
|
56
|
+
host
|
57
|
+
end
|
58
|
+
|
59
|
+
sig { returns(T.nilable(String)) }
|
60
|
+
def line_number
|
61
|
+
fragment
|
62
|
+
end
|
63
|
+
|
57
64
|
sig { params(v: T.nilable(String)).void }
|
58
65
|
def set_path(v) # rubocop:disable Naming/AccessorMethodName
|
59
66
|
return if v.nil?
|
data/lib/tapioca/loaders/dsl.rb
CHANGED
@@ -10,16 +10,32 @@ module Tapioca
|
|
10
10
|
extend T::Sig
|
11
11
|
|
12
12
|
sig do
|
13
|
-
params(
|
13
|
+
params(
|
14
|
+
tapioca_path: String,
|
15
|
+
eager_load: T::Boolean,
|
16
|
+
app_root: String,
|
17
|
+
halt_upon_load_error: T::Boolean,
|
18
|
+
lsp_addon: T::Boolean,
|
19
|
+
).void
|
14
20
|
end
|
15
|
-
def load_application(
|
21
|
+
def load_application(
|
22
|
+
tapioca_path:,
|
23
|
+
eager_load: true,
|
24
|
+
app_root: ".",
|
25
|
+
halt_upon_load_error: true,
|
26
|
+
lsp_addon: false
|
27
|
+
)
|
16
28
|
loader = new(
|
17
29
|
tapioca_path: tapioca_path,
|
18
30
|
eager_load: eager_load,
|
19
31
|
app_root: app_root,
|
20
32
|
halt_upon_load_error: halt_upon_load_error,
|
21
33
|
)
|
22
|
-
|
34
|
+
if lsp_addon
|
35
|
+
loader.load_dsl_extensions_and_compilers
|
36
|
+
else
|
37
|
+
loader.load
|
38
|
+
end
|
23
39
|
end
|
24
40
|
end
|
25
41
|
|
@@ -30,6 +46,12 @@ module Tapioca
|
|
30
46
|
load_dsl_compilers
|
31
47
|
end
|
32
48
|
|
49
|
+
sig { void }
|
50
|
+
def load_dsl_extensions_and_compilers
|
51
|
+
load_dsl_extensions
|
52
|
+
load_dsl_compilers
|
53
|
+
end
|
54
|
+
|
33
55
|
protected
|
34
56
|
|
35
57
|
sig do
|
@@ -63,7 +85,7 @@ module Tapioca
|
|
63
85
|
def load_dsl_compilers
|
64
86
|
say("Loading DSL compiler classes... ")
|
65
87
|
|
66
|
-
# Load all built-in compilers
|
88
|
+
# Load all built-in compilers before any custom compilers
|
67
89
|
Dir.glob("#{Tapioca::LIB_ROOT_DIR}/tapioca/dsl/compilers/*.rb").each do |compiler|
|
68
90
|
require File.expand_path(compiler)
|
69
91
|
end
|
data/lib/tapioca/version.rb
CHANGED
metadata
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tapioca
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.16.
|
4
|
+
version: 0.16.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ufuk Kayserilioglu
|
8
8
|
- Alan Wu
|
9
9
|
- Alexandre Terrasa
|
10
10
|
- Peter Zhu
|
11
|
-
autorequire:
|
12
11
|
bindir: exe
|
13
12
|
cert_chain: []
|
14
|
-
date:
|
13
|
+
date: 2025-01-06 00:00:00.000000000 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: bundler
|
@@ -125,7 +124,6 @@ dependencies:
|
|
125
124
|
- - ">="
|
126
125
|
- !ruby/object:Gem::Version
|
127
126
|
version: '0'
|
128
|
-
description:
|
129
127
|
email:
|
130
128
|
- ruby@shopify.com
|
131
129
|
executables:
|
@@ -136,6 +134,8 @@ files:
|
|
136
134
|
- LICENSE.txt
|
137
135
|
- README.md
|
138
136
|
- exe/tapioca
|
137
|
+
- lib/ruby_lsp/tapioca/addon.rb
|
138
|
+
- lib/ruby_lsp/tapioca/server_addon.rb
|
139
139
|
- lib/tapioca.rb
|
140
140
|
- lib/tapioca/bundler_ext/auto_require_hook.rb
|
141
141
|
- lib/tapioca/cli.rb
|
@@ -269,7 +269,6 @@ licenses:
|
|
269
269
|
- MIT
|
270
270
|
metadata:
|
271
271
|
allowed_push_host: https://rubygems.org
|
272
|
-
post_install_message:
|
273
272
|
rdoc_options: []
|
274
273
|
require_paths:
|
275
274
|
- lib
|
@@ -284,8 +283,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
284
283
|
- !ruby/object:Gem::Version
|
285
284
|
version: '0'
|
286
285
|
requirements: []
|
287
|
-
rubygems_version: 3.
|
288
|
-
signing_key:
|
286
|
+
rubygems_version: 3.6.2
|
289
287
|
specification_version: 4
|
290
288
|
summary: A Ruby Interface file generator for gems, core types and the Ruby standard
|
291
289
|
library
|