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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc2c2c6ae059634316e511dce8839f7e4930b0263093e6250612aa7d6aae582d
4
- data.tar.gz: 7ee41089cca733c46e3d3c9dca9384c1e8a516fd7ebb0fb347eb31935da8d3ee
3
+ metadata.gz: 80c01f44b0600c3a471756da73e43322f23a951c8f3ebc15c922173a0ccf4d3c
4
+ data.tar.gz: c778cfa78b0494513f5ad329e432c534486c3ba75fcf83969462a874c7dc7654
5
5
  SHA512:
6
- metadata.gz: 00ecd9606853f9bf27821df7c20b74af4898d84d5b3a32472a5156c2d0ba353b4ab53e7ddc4d21474a2b1599409aecad05693ac6713100798a4cf77dc2d50b83
7
- data.tar.gz: 4b8d2455cf3cd89c0744d4e266331ee61c3ca585380db1da348bafaa014fbeaa7483401100f1148f8c89fe21119c9a555e9af86be3699ba2733f48cab1929901
6
+ metadata.gz: f12a7fbe0d64109a5de03d8a98f564413bee79dc0db1ced8c1eaeae8dae73e68c73e8e76a188a06ddfefa333b11024d606359b7f706d13e15dacea081b0a19ea
7
+ data.tar.gz: 99ef8194c8dab5e776b591992aefa8b59b0b8e2d8927caecfc68d63bf7bb4c7687a92af772c6f8ac5fcfbdd8ebe1f9c1b748a2334b8207d233939d35d02d2b26
@@ -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
- ObjectSpace.each_object(Class),
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
- ObjectSpace.each_object(Module),
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("block", type: "T.nilable(T.proc.bind(#{constant_name}).void)"),
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.map do |column|
92
- column_type_for(column)
93
- end.map do |tuple|
94
- "[#{tuple.join(", ")}]"
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),
@@ -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
- # try to parse a method definition with this name
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 = :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: DEFAULT_PARSER.escape("/#{gem_version}/#{path}"),
50
+ path: PARSER.escape("/#{gem_version}/#{path}"),
44
51
  fragment: line_number,
45
52
  }
46
53
  )
@@ -23,6 +23,7 @@ require "tempfile"
23
23
  require "thor"
24
24
  require "yaml"
25
25
  require "yard-sorbet"
26
+ require "prism"
26
27
 
27
28
  require "tapioca/runtime/dynamic_mixin_compiler"
28
29
  require "tapioca/sorbet_ext/backcompat_patches"
@@ -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)>". If none is found, it returns the location
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 { |loc| REQUIRED_FROM_LABELS.include?(loc.label) }
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 || ""
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.16.2"
5
+ VERSION = "0.16.4"
6
6
  end
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.2
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-08-29 00:00:00.000000000 Z
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.17
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