tapioca 0.14.4 → 0.15.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/tapioca/commands/annotations.rb +26 -14
- data/lib/tapioca/dsl/compilers/aasm.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +1 -1
- data/lib/tapioca/dsl/compilers/identity_cache.rb +1 -1
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +23 -9
- data/lib/tapioca/gem_info.rb +18 -0
- data/lib/tapioca/internal.rb +7 -6
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +1 -0
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12203d783fb506aff6b357dc3dc02fd7f9d9e7147fd2fb715329a5311ff96bf6
|
4
|
+
data.tar.gz: 0ac885bad578baec1701f6b2f46e48ca5f12e4c5d8fd9840d63195291400a430
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f42230c7ca6b96bb6ed299faa2b4c8d8f87ae44244a6501006c55b22c6307b9a0b777cdbb8d713f6ee3320d44502428c17ddf86dd85929be842433557d3e3793
|
7
|
+
data.tar.gz: df6f57d7c1446e70bd759bc84c5fa06f5985e01783be8d8f7655e06e3045c5de17cc1e931d4f0b5d78bc19f867de61dc228c77eb2f4df325038e09d908e2efd0
|
@@ -46,22 +46,22 @@ module Tapioca
|
|
46
46
|
GitAttributes.create_vendored_attribute_file(@outpath)
|
47
47
|
end
|
48
48
|
|
49
|
-
sig { returns(T::Array[
|
49
|
+
sig { returns(T::Array[GemInfo]) }
|
50
50
|
def list_gemfile_gems
|
51
51
|
say("Listing gems from Gemfile.lock... ", [:blue, :bold])
|
52
52
|
gemfile = Bundler.read_file("Gemfile.lock")
|
53
53
|
parser = Bundler::LockfileParser.new(gemfile)
|
54
|
-
|
54
|
+
gem_info = parser.specs.map { |spec| GemInfo.from_spec(spec) }
|
55
55
|
say("Done", :green)
|
56
|
-
|
56
|
+
gem_info
|
57
57
|
end
|
58
58
|
|
59
|
-
sig { params(project_gems: T::Array[
|
59
|
+
sig { params(project_gems: T::Array[GemInfo]).void }
|
60
60
|
def remove_expired_annotations(project_gems)
|
61
61
|
say("Removing annotations for gems that have been removed... ", [:blue, :bold])
|
62
62
|
|
63
63
|
annotations = Pathname.glob(@outpath.join("*.rbi")).map { |f| f.basename(".*").to_s }
|
64
|
-
expired = annotations - project_gems
|
64
|
+
expired = annotations - project_gems.map(&:name)
|
65
65
|
|
66
66
|
if expired.empty?
|
67
67
|
say(" Nothing to do")
|
@@ -109,14 +109,14 @@ module Tapioca
|
|
109
109
|
index
|
110
110
|
end
|
111
111
|
|
112
|
-
sig { params(
|
113
|
-
def fetch_annotations(
|
112
|
+
sig { params(project_gems: T::Array[GemInfo]).returns(T::Array[String]) }
|
113
|
+
def fetch_annotations(project_gems)
|
114
114
|
say("Fetching gem annotations from central repository... ", [:blue, :bold])
|
115
|
-
fetchable_gems = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[
|
115
|
+
fetchable_gems = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[GemInfo, T::Array[String]])
|
116
116
|
|
117
|
-
|
117
|
+
project_gems.each_with_object(fetchable_gems) do |gem_info, hash|
|
118
118
|
@indexes.each do |uri, index|
|
119
|
-
T.must(hash[
|
119
|
+
T.must(hash[gem_info]) << uri if index.has_gem?(gem_info.name)
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
@@ -127,13 +127,16 @@ module Tapioca
|
|
127
127
|
end
|
128
128
|
|
129
129
|
say("\n")
|
130
|
-
fetched_gems = fetchable_gems.select { |
|
130
|
+
fetched_gems = fetchable_gems.select { |gem_info, repo_uris| fetch_annotation(repo_uris, gem_info) }
|
131
131
|
say("\nDone", :green)
|
132
|
-
fetched_gems.keys.sort
|
132
|
+
fetched_gems.keys.map(&:name).sort
|
133
133
|
end
|
134
134
|
|
135
|
-
sig { params(repo_uris: T::Array[String],
|
136
|
-
def fetch_annotation(repo_uris,
|
135
|
+
sig { params(repo_uris: T::Array[String], gem_info: GemInfo).void }
|
136
|
+
def fetch_annotation(repo_uris, gem_info)
|
137
|
+
gem_name = gem_info.name
|
138
|
+
gem_version = gem_info.version
|
139
|
+
|
137
140
|
contents = repo_uris.map do |repo_uri|
|
138
141
|
fetch_file(repo_uri, "#{CENTRAL_REPO_ANNOTATIONS_DIR}/#{gem_name}.rbi")
|
139
142
|
end
|
@@ -142,6 +145,7 @@ module Tapioca
|
|
142
145
|
return unless content
|
143
146
|
|
144
147
|
content = apply_typed_override(gem_name, content)
|
148
|
+
content = filter_versions(gem_version, content)
|
145
149
|
content = add_header(gem_name, content)
|
146
150
|
|
147
151
|
say("\n Fetched #{set_color(gem_name, :yellow, :bold)}", :green)
|
@@ -221,6 +225,14 @@ module Tapioca
|
|
221
225
|
Spoom::Sorbet::Sigils.update_sigil(content, strictness)
|
222
226
|
end
|
223
227
|
|
228
|
+
sig { params(gem_version: ::Gem::Version, content: String).returns(String) }
|
229
|
+
def filter_versions(gem_version, content)
|
230
|
+
rbi = RBI::Parser.parse_string(content)
|
231
|
+
rbi.filter_versions!(gem_version)
|
232
|
+
|
233
|
+
rbi.string
|
234
|
+
end
|
235
|
+
|
224
236
|
sig { params(gem_name: String, contents: T::Array[String]).returns(T.nilable(String)) }
|
225
237
|
def merge_files(gem_name, contents)
|
226
238
|
return if contents.empty?
|
@@ -147,7 +147,7 @@ module Tapioca
|
|
147
147
|
machine.create_method(
|
148
148
|
method,
|
149
149
|
parameters: [
|
150
|
-
|
150
|
+
create_rest_param("callbacks", type: "T.any(String, Symbol, T::Class[T.anything], Proc)"),
|
151
151
|
create_block_param("block", type: "T.nilable(T.proc.bind(#{constant_name}).void)"),
|
152
152
|
],
|
153
153
|
)
|
@@ -190,7 +190,7 @@ module Tapioca
|
|
190
190
|
# Grab all Spawn methods
|
191
191
|
query_methods |= ActiveRecord::SpawnMethods.instance_methods(false)
|
192
192
|
# Remove the ones we know are private API
|
193
|
-
query_methods -= [:arel, :build_subquery, :construct_join_dependency, :extensions, :spawn]
|
193
|
+
query_methods -= [:all, :arel, :build_subquery, :construct_join_dependency, :extensions, :spawn]
|
194
194
|
# Remove "group" which needs a custom return type for GroupChains
|
195
195
|
query_methods -= [:group]
|
196
196
|
# Remove "where" which needs a custom return type for WhereChains
|
@@ -104,7 +104,7 @@ module Tapioca
|
|
104
104
|
|
105
105
|
column = @constant.columns_hash[column_name]
|
106
106
|
column_type = @constant.attribute_types[column_name]
|
107
|
-
getter_type = type_for_activerecord_value(column_type)
|
107
|
+
getter_type = type_for_activerecord_value(column_type, column_nullability: !!column&.null)
|
108
108
|
setter_type =
|
109
109
|
case column_type
|
110
110
|
when ActiveRecord::Enum::EnumType
|
@@ -121,8 +121,8 @@ module Tapioca
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
-
sig { params(column_type: T.untyped).returns(String) }
|
125
|
-
def type_for_activerecord_value(column_type)
|
124
|
+
sig { params(column_type: T.untyped, column_nullability: T::Boolean).returns(String) }
|
125
|
+
def type_for_activerecord_value(column_type, column_nullability:)
|
126
126
|
case column_type
|
127
127
|
when ->(type) { defined?(MoneyColumn) && MoneyColumn::ActiveRecordType === type }
|
128
128
|
"::Money"
|
@@ -133,11 +133,12 @@ module Tapioca
|
|
133
133
|
}
|
134
134
|
# Reflect to see if `ActiveModel::Type::Value` is being used first.
|
135
135
|
getter_type = Tapioca::Dsl::Helpers::ActiveModelTypeHelper.type_for(column_type)
|
136
|
-
return getter_type unless getter_type == "T.untyped"
|
137
136
|
|
138
|
-
#
|
137
|
+
# Fallback to String as `ActiveRecord::Encryption::EncryptedAttributeType` inherits from
|
139
138
|
# `ActiveRecord::Type::Text` which inherits from `ActiveModel::Type::String`.
|
140
|
-
"::String"
|
139
|
+
return "::String" if getter_type == "T.untyped"
|
140
|
+
|
141
|
+
as_non_nilable_if_persisted_and_not_nullable(getter_type, column_nullability:)
|
141
142
|
when ActiveRecord::Type::String
|
142
143
|
"::String"
|
143
144
|
when ActiveRecord::Type::Date
|
@@ -160,7 +161,7 @@ module Tapioca
|
|
160
161
|
defined?(ActiveRecord::Normalization::NormalizedValueType) &&
|
161
162
|
ActiveRecord::Normalization::NormalizedValueType === type
|
162
163
|
}
|
163
|
-
type_for_activerecord_value(column_type.cast_type)
|
164
|
+
type_for_activerecord_value(column_type.cast_type, column_nullability:)
|
164
165
|
when ->(type) {
|
165
166
|
defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Uuid) &&
|
166
167
|
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Uuid === type
|
@@ -180,12 +181,25 @@ module Tapioca
|
|
180
181
|
defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array) &&
|
181
182
|
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array === type
|
182
183
|
}
|
183
|
-
"T::Array[#{type_for_activerecord_value(column_type.subtype)}]"
|
184
|
+
"T::Array[#{type_for_activerecord_value(column_type.subtype, column_nullability:)}]"
|
184
185
|
else
|
185
|
-
|
186
|
+
as_non_nilable_if_persisted_and_not_nullable(
|
187
|
+
ActiveModelTypeHelper.type_for(column_type),
|
188
|
+
column_nullability: column_nullability,
|
189
|
+
)
|
186
190
|
end
|
187
191
|
end
|
188
192
|
|
193
|
+
sig { params(base_type: String, column_nullability: T::Boolean).returns(String) }
|
194
|
+
def as_non_nilable_if_persisted_and_not_nullable(base_type, column_nullability:)
|
195
|
+
# It's possible that when ActiveModel::Type::Value is used, the signature being reflected on in
|
196
|
+
# ActiveModelTypeHelper.type_for(type_value) may say the type can be nilable. However, if the type is
|
197
|
+
# persisted and the column is not nullable, we can assume it's not nilable.
|
198
|
+
return as_non_nilable_type(base_type) if @column_type_option.persisted? && !column_nullability
|
199
|
+
|
200
|
+
base_type
|
201
|
+
end
|
202
|
+
|
189
203
|
sig { params(column_type: ActiveRecord::Enum::EnumType).returns(String) }
|
190
204
|
def enum_setter_type(column_type)
|
191
205
|
# In Rails < 7 this method is private. When support for that is dropped we can call the method directly
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Tapioca
|
5
|
+
class GemInfo < T::Struct
|
6
|
+
const :name, String
|
7
|
+
const :version, ::Gem::Version
|
8
|
+
|
9
|
+
class << self
|
10
|
+
extend(T::Sig)
|
11
|
+
|
12
|
+
sig { params(spec: Bundler::LazySpecification).returns(GemInfo) }
|
13
|
+
def from_spec(spec)
|
14
|
+
new(name: spec.name, version: spec.version)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/tapioca/internal.rb
CHANGED
@@ -19,24 +19,24 @@ require "netrc"
|
|
19
19
|
require "parallel"
|
20
20
|
require "pathname"
|
21
21
|
require "shellwords"
|
22
|
-
require "spoom"
|
23
22
|
require "tempfile"
|
24
23
|
require "thor"
|
25
24
|
require "yaml"
|
26
25
|
require "yard-sorbet"
|
27
26
|
|
28
27
|
require "tapioca/runtime/dynamic_mixin_compiler"
|
29
|
-
require "tapioca/helpers/gem_helper"
|
30
|
-
|
31
|
-
require "tapioca/helpers/git_attributes"
|
32
|
-
require "tapioca/helpers/sorbet_helper"
|
33
|
-
require "tapioca/helpers/rbi_helper"
|
34
28
|
require "tapioca/sorbet_ext/backcompat_patches"
|
35
29
|
require "tapioca/sorbet_ext/name_patch"
|
36
30
|
require "tapioca/sorbet_ext/generic_name_patch"
|
37
31
|
require "tapioca/sorbet_ext/proc_bind_patch"
|
38
32
|
require "tapioca/runtime/generic_type_registry"
|
39
33
|
|
34
|
+
require "spoom"
|
35
|
+
require "tapioca/helpers/gem_helper"
|
36
|
+
require "tapioca/helpers/git_attributes"
|
37
|
+
require "tapioca/helpers/sorbet_helper"
|
38
|
+
require "tapioca/helpers/rbi_helper"
|
39
|
+
|
40
40
|
require "tapioca/helpers/source_uri"
|
41
41
|
require "tapioca/helpers/cli_helper"
|
42
42
|
require "tapioca/helpers/config_helper"
|
@@ -45,6 +45,7 @@ require "tapioca/helpers/env_helper"
|
|
45
45
|
|
46
46
|
require "tapioca/repo_index"
|
47
47
|
require "tapioca/gemfile"
|
48
|
+
require "tapioca/gem_info"
|
48
49
|
require "tapioca/executor"
|
49
50
|
|
50
51
|
require "tapioca/static/symbol_table_parser"
|
data/lib/tapioca/version.rb
CHANGED
data/lib/tapioca.rb
CHANGED
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tapioca
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.1
|
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:
|
11
|
+
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2024-
|
14
|
+
date: 2024-07-10 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -131,7 +131,7 @@ dependencies:
|
|
131
131
|
- - ">="
|
132
132
|
- !ruby/object:Gem::Version
|
133
133
|
version: '0'
|
134
|
-
description:
|
134
|
+
description:
|
135
135
|
email:
|
136
136
|
- ruby@shopify.com
|
137
137
|
executables:
|
@@ -228,6 +228,7 @@ files:
|
|
228
228
|
- lib/tapioca/gem/listeners/subconstants.rb
|
229
229
|
- lib/tapioca/gem/listeners/yard_doc.rb
|
230
230
|
- lib/tapioca/gem/pipeline.rb
|
231
|
+
- lib/tapioca/gem_info.rb
|
231
232
|
- lib/tapioca/gemfile.rb
|
232
233
|
- lib/tapioca/helpers/cli_helper.rb
|
233
234
|
- lib/tapioca/helpers/config_helper.rb
|
@@ -273,7 +274,7 @@ licenses:
|
|
273
274
|
- MIT
|
274
275
|
metadata:
|
275
276
|
allowed_push_host: https://rubygems.org
|
276
|
-
post_install_message:
|
277
|
+
post_install_message:
|
277
278
|
rdoc_options: []
|
278
279
|
require_paths:
|
279
280
|
- lib
|
@@ -288,8 +289,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
288
289
|
- !ruby/object:Gem::Version
|
289
290
|
version: '0'
|
290
291
|
requirements: []
|
291
|
-
rubygems_version: 3.5.
|
292
|
-
signing_key:
|
292
|
+
rubygems_version: 3.5.14
|
293
|
+
signing_key:
|
293
294
|
specification_version: 4
|
294
295
|
summary: A Ruby Interface file generator for gems, core types and the Ruby standard
|
295
296
|
library
|