tapioca 0.16.2 → 0.16.4
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/dsl/compiler.rb +35 -2
- data/lib/tapioca/dsl/compilers/aasm.rb +41 -1
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +32 -5
- data/lib/tapioca/dsl/pipeline.rb +1 -0
- data/lib/tapioca/helpers/rbi_helper.rb +2 -26
- data/lib/tapioca/helpers/source_uri.rb +8 -1
- data/lib/tapioca/internal.rb +1 -0
- data/lib/tapioca/runtime/reflection.rb +8 -2
- data/lib/tapioca/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80c01f44b0600c3a471756da73e43322f23a951c8f3ebc15c922173a0ccf4d3c
|
4
|
+
data.tar.gz: c778cfa78b0494513f5ad329e432c534486c3ba75fcf83969462a874c7dc7654
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f12a7fbe0d64109a5de03d8a98f564413bee79dc0db1ced8c1eaeae8dae73e68c73e8e76a188a06ddfefa333b11024d606359b7f706d13e15dacea081b0a19ea
|
7
|
+
data.tar.gz: 99ef8194c8dab5e776b591992aefa8b59b0b8e2d8927caecfc68d63bf7bb4c7687a92af772c6f8ac5fcfbdd8ebe1f9c1b748a2334b8207d233939d35d02d2b26
|
data/lib/tapioca/dsl/compiler.rb
CHANGED
@@ -25,6 +25,8 @@ module Tapioca
|
|
25
25
|
sig { returns(T::Hash[String, T.untyped]) }
|
26
26
|
attr_reader :options
|
27
27
|
|
28
|
+
@@requested_constants = T.let([], T::Array[Module]) # rubocop:disable Style/ClassVars
|
29
|
+
|
28
30
|
class << self
|
29
31
|
extend T::Sig
|
30
32
|
|
@@ -44,12 +46,39 @@ module Tapioca
|
|
44
46
|
)
|
45
47
|
end
|
46
48
|
|
49
|
+
sig { params(constants: T::Array[Module]).void }
|
50
|
+
def requested_constants=(constants)
|
51
|
+
@@requested_constants = constants # rubocop:disable Style/ClassVars
|
52
|
+
end
|
53
|
+
|
47
54
|
private
|
48
55
|
|
56
|
+
sig do
|
57
|
+
type_parameters(:U)
|
58
|
+
.params(klass: T.all(T::Class[T.anything], T.type_parameter(:U)))
|
59
|
+
.returns(T::Array[T.type_parameter(:U)])
|
60
|
+
end
|
61
|
+
def descendants_of(klass)
|
62
|
+
if @@requested_constants.any?
|
63
|
+
T.cast(
|
64
|
+
@@requested_constants.select do |k|
|
65
|
+
k < klass && !k.singleton_class?
|
66
|
+
end,
|
67
|
+
T::Array[T.type_parameter(:U)],
|
68
|
+
)
|
69
|
+
else
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
49
74
|
sig { returns(T::Enumerable[T::Class[T.anything]]) }
|
50
75
|
def all_classes
|
51
76
|
@all_classes ||= T.let(
|
52
|
-
|
77
|
+
if @@requested_constants.any?
|
78
|
+
@@requested_constants.grep(Class)
|
79
|
+
else
|
80
|
+
ObjectSpace.each_object(Class)
|
81
|
+
end,
|
53
82
|
T.nilable(T::Enumerable[T::Class[T.anything]]),
|
54
83
|
)
|
55
84
|
end
|
@@ -57,7 +86,11 @@ module Tapioca
|
|
57
86
|
sig { returns(T::Enumerable[Module]) }
|
58
87
|
def all_modules
|
59
88
|
@all_modules ||= T.let(
|
60
|
-
|
89
|
+
if @@requested_constants.any?
|
90
|
+
@@requested_constants.select { |k| k.is_a?(Module) }
|
91
|
+
else
|
92
|
+
ObjectSpace.each_object(Module)
|
93
|
+
end,
|
61
94
|
T.nilable(T::Enumerable[Module]),
|
62
95
|
)
|
63
96
|
end
|
@@ -69,6 +69,17 @@ module Tapioca
|
|
69
69
|
T::Array[String],
|
70
70
|
)
|
71
71
|
|
72
|
+
TRANSITION_CALLBACKS =
|
73
|
+
T.let(
|
74
|
+
[
|
75
|
+
"on_transition",
|
76
|
+
"guard",
|
77
|
+
"after",
|
78
|
+
"success",
|
79
|
+
].freeze,
|
80
|
+
T::Array[String],
|
81
|
+
)
|
82
|
+
|
72
83
|
ConstantType = type_member { { fixed: T.all(T::Class[::AASM], ::AASM::ClassMethods) } }
|
73
84
|
|
74
85
|
sig { override.void }
|
@@ -162,8 +173,37 @@ module Tapioca
|
|
162
173
|
method,
|
163
174
|
parameters: [
|
164
175
|
create_opt_param("symbol", type: "T.nilable(Symbol)", default: "nil"),
|
165
|
-
create_block_param(
|
176
|
+
create_block_param(
|
177
|
+
"block",
|
178
|
+
type: "T.nilable(T.proc.bind(#{constant_name}).params(opts: T.untyped).void)",
|
179
|
+
),
|
180
|
+
],
|
181
|
+
)
|
182
|
+
end
|
183
|
+
|
184
|
+
event.create_method(
|
185
|
+
"transitions",
|
186
|
+
parameters: [
|
187
|
+
create_opt_param("definitions", default: "nil", type: "T.untyped"),
|
188
|
+
create_block_param("block", type: "T.nilable(T.proc.bind(PrivateAASMTransition).void)"),
|
189
|
+
],
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
193
|
+
machine.create_class("PrivateAASMTransition", superclass_name: "AASM::Core::Transition") do |transition|
|
194
|
+
TRANSITION_CALLBACKS.each do |method|
|
195
|
+
return_type = "T.untyped"
|
196
|
+
return_type = "T::Boolean" if method == "guard"
|
197
|
+
|
198
|
+
transition.create_method(
|
199
|
+
method,
|
200
|
+
parameters: [
|
201
|
+
create_block_param(
|
202
|
+
"block",
|
203
|
+
type: "T.nilable(T.proc.bind(#{constant_name}).params(opts: T.untyped).void)",
|
204
|
+
),
|
166
205
|
],
|
206
|
+
return_type: return_type,
|
167
207
|
)
|
168
208
|
end
|
169
209
|
end
|
@@ -88,17 +88,24 @@ module Tapioca
|
|
88
88
|
sig { returns([String, String]) }
|
89
89
|
def id_type
|
90
90
|
if @constant.respond_to?(:composite_primary_key?) && T.unsafe(@constant).composite_primary_key?
|
91
|
-
@constant.primary_key
|
92
|
-
|
93
|
-
|
94
|
-
|
91
|
+
primary_key_columns = @constant.primary_key
|
92
|
+
|
93
|
+
getters = []
|
94
|
+
setters = []
|
95
|
+
|
96
|
+
primary_key_columns.each do |column|
|
97
|
+
getter, setter = column_type_for(column)
|
98
|
+
getters << getter
|
99
|
+
setters << setter
|
95
100
|
end
|
101
|
+
|
102
|
+
["[#{getters.join(", ")}]", "[#{setters.join(", ")}]"]
|
96
103
|
else
|
97
104
|
column_type_for(@constant.primary_key)
|
98
105
|
end
|
99
106
|
end
|
100
107
|
|
101
|
-
sig { params(column_name: String).returns([String, String]) }
|
108
|
+
sig { params(column_name: T.nilable(String)).returns([String, String]) }
|
102
109
|
def column_type_for(column_name)
|
103
110
|
return ["T.untyped", "T.untyped"] if @column_type_option.untyped?
|
104
111
|
|
@@ -179,11 +186,31 @@ module Tapioca
|
|
179
186
|
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Hstore === type
|
180
187
|
}
|
181
188
|
"T::Hash[::String, ::String]"
|
189
|
+
when ->(type) {
|
190
|
+
defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Interval) &&
|
191
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Interval === type
|
192
|
+
}
|
193
|
+
"::ActiveSupport::Duration"
|
182
194
|
when ->(type) {
|
183
195
|
defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array) &&
|
184
196
|
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array === type
|
185
197
|
}
|
186
198
|
"T::Array[#{type_for_activerecord_value(column_type.subtype, column_nullability:)}]"
|
199
|
+
when ->(type) {
|
200
|
+
defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bit) &&
|
201
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bit === type
|
202
|
+
}
|
203
|
+
"::String"
|
204
|
+
when ->(type) {
|
205
|
+
defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::BitVarying) &&
|
206
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::BitVarying === type
|
207
|
+
}
|
208
|
+
"::String"
|
209
|
+
when ->(type) {
|
210
|
+
defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range) &&
|
211
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range === type
|
212
|
+
}
|
213
|
+
"T::Range[#{type_for_activerecord_value(column_type.subtype, column_nullability:)}]"
|
187
214
|
else
|
188
215
|
as_non_nilable_if_persisted_and_not_nullable(
|
189
216
|
ActiveModelTypeHelper.type_for(column_type),
|
data/lib/tapioca/dsl/pipeline.rb
CHANGED
@@ -145,6 +145,7 @@ module Tapioca
|
|
145
145
|
).returns(T::Set[Module])
|
146
146
|
end
|
147
147
|
def gather_constants(requested_constants, requested_paths, skipped_constants)
|
148
|
+
Compiler.requested_constants = requested_constants
|
148
149
|
constants = Set.new.compare_by_identity
|
149
150
|
active_compilers.each do |compiler|
|
150
151
|
constants.merge(compiler.processable_constants)
|
@@ -107,36 +107,12 @@ module Tapioca
|
|
107
107
|
|
108
108
|
sig { params(name: String).returns(T::Boolean) }
|
109
109
|
def valid_method_name?(name)
|
110
|
-
|
111
|
-
iseq = RubyVM::InstructionSequence.compile("def #{name}; end", nil, nil, 0, false)
|
112
|
-
# pull out the first operation in the instruction sequence and its first argument
|
113
|
-
op, arg, _data = iseq.to_a.dig(-1, 0)
|
114
|
-
# make sure that the operation is a method definition and the method that was
|
115
|
-
# defined has the expected name, for example, for `def !foo; end` we don't get
|
116
|
-
# a syntax error but instead get a method defined as `"foo"`
|
117
|
-
op == :definemethod && arg == name.to_sym
|
118
|
-
rescue SyntaxError
|
119
|
-
false
|
110
|
+
Prism.parse_success?("def self.#{name}(a); end")
|
120
111
|
end
|
121
112
|
|
122
113
|
sig { params(name: String).returns(T::Boolean) }
|
123
114
|
def valid_parameter_name?(name)
|
124
|
-
sentinel_method_name
|
125
|
-
# try to parse a method definition with this name as the name of a
|
126
|
-
# keyword parameter. If we use a positional parameter, then parameter names
|
127
|
-
# like `&` (and maybe others) will be treated like `def foo(&); end` and will
|
128
|
-
# thus be considered valid. Using a required keyword parameter prevents that
|
129
|
-
# confusion between Ruby syntax and parameter name.
|
130
|
-
iseq = RubyVM::InstructionSequence.compile("def #{sentinel_method_name}(#{name}:); end", nil, nil, 0, false)
|
131
|
-
# pull out the first operation in the instruction sequence and its first argument and data
|
132
|
-
op, arg, data = iseq.to_a.dig(-1, 0)
|
133
|
-
# make sure that:
|
134
|
-
# 1. a method was defined, and
|
135
|
-
# 2. the method has the expected method name, and
|
136
|
-
# 3. the method has a keyword parameter with the expected name
|
137
|
-
op == :definemethod && arg == sentinel_method_name && data.dig(11, :keyword, 0) == name.to_sym
|
138
|
-
rescue SyntaxError
|
139
|
-
false
|
115
|
+
Prism.parse_success?("def sentinel_method_name(#{name}:); end")
|
140
116
|
end
|
141
117
|
end
|
142
118
|
end
|
@@ -18,6 +18,13 @@ module URI
|
|
18
18
|
T::Array[Symbol],
|
19
19
|
)
|
20
20
|
|
21
|
+
# `uri` for Ruby 3.4 switched the default parser from RFC2396 to RFC3986. The new parser emits a deprecation
|
22
|
+
# warning on a few methods and delegates them to RFC2396, namely `extract`/`make_regexp`/`escape`/`unescape`.
|
23
|
+
# On earlier versions of the uri gem, the RFC2396_PARSER constant doesn't exist, so it needs some special
|
24
|
+
# handling to select a parser that doesn't emit deprecations. While it was backported to Ruby 3.1, users may
|
25
|
+
# have the uri gem in their own bundle and thus not use a compatible version.
|
26
|
+
PARSER = T.let(const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER, RFC2396_Parser)
|
27
|
+
|
21
28
|
alias_method(:gem_name, :host)
|
22
29
|
alias_method(:line_number, :fragment)
|
23
30
|
|
@@ -40,7 +47,7 @@ module URI
|
|
40
47
|
{
|
41
48
|
scheme: "source",
|
42
49
|
host: gem_name,
|
43
|
-
path:
|
50
|
+
path: PARSER.escape("/#{gem_version}/#{path}"),
|
44
51
|
fragment: line_number,
|
45
52
|
}
|
46
53
|
)
|
data/lib/tapioca/internal.rb
CHANGED
@@ -178,13 +178,19 @@ module Tapioca
|
|
178
178
|
end
|
179
179
|
|
180
180
|
# Examines the call stack to identify the closest location where a "require" is performed
|
181
|
-
# by searching for the label "<top (required)>"
|
181
|
+
# by searching for the label "<top (required)>" or "block in <class:...>" in the
|
182
|
+
# case of an ActiveSupport.on_load hook. If none is found, it returns the location
|
182
183
|
# labeled "<main>", which is the original call site.
|
183
184
|
sig { params(locations: T.nilable(T::Array[Thread::Backtrace::Location])).returns(String) }
|
184
185
|
def resolve_loc(locations)
|
185
186
|
return "" unless locations
|
186
187
|
|
187
|
-
resolved_loc = locations.find
|
188
|
+
resolved_loc = locations.find do |loc|
|
189
|
+
label = loc.label
|
190
|
+
next unless label
|
191
|
+
|
192
|
+
REQUIRED_FROM_LABELS.include?(label) || label.start_with?("block in <class:")
|
193
|
+
end
|
188
194
|
return "" unless resolved_loc
|
189
195
|
|
190
196
|
resolved_loc.absolute_path || ""
|
data/lib/tapioca/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ufuk Kayserilioglu
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2024-
|
14
|
+
date: 2024-11-07 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -284,7 +284,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
284
284
|
- !ruby/object:Gem::Version
|
285
285
|
version: '0'
|
286
286
|
requirements: []
|
287
|
-
rubygems_version: 3.5.
|
287
|
+
rubygems_version: 3.5.23
|
288
288
|
signing_key:
|
289
289
|
specification_version: 4
|
290
290
|
summary: A Ruby Interface file generator for gems, core types and the Ruby standard
|