tapioca 0.14.4 → 0.15.1
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/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
|