Skip to content

Commit

Permalink
ActiveRecordRelations support for generic definitions in AR models
Browse files Browse the repository at this point in the history
  • Loading branch information
gustavompo committed Jul 31, 2024
1 parent 21a9a6d commit 5c2ce79
Show file tree
Hide file tree
Showing 4 changed files with 811 additions and 16 deletions.
11 changes: 10 additions & 1 deletion lib/tapioca/dsl/compilers/active_record_relations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,16 @@ def common_relation_methods_module

sig { returns(String) }
def constant_name
@constant_name ||= T.let(T.must(qualified_name_of(constant)), T.nilable(String))
@constant_name ||= T.let(constant_name_of(constant), T.nilable(String))
end

sig { params(constant: ConstantType).returns(String) }
def constant_name_of(constant)
if T::Generic === constant
generic_name_of(constant)
else
T.must(qualified_name_of(constant))
end
end

sig { params(method_name: Symbol).returns(T::Boolean) }
Expand Down
15 changes: 0 additions & 15 deletions lib/tapioca/gem/pipeline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -475,21 +475,6 @@ def generic_name_of(constant)

"#{type_name}[#{type_variable_names}]"
end

sig { params(constant: Module, class_name: T.nilable(String)).returns(T.nilable(String)) }
def name_of_proxy_target(constant, class_name)
return unless class_name == "ActiveSupport::Deprecation::DeprecatedConstantProxy"

# We are dealing with a ActiveSupport::Deprecation::DeprecatedConstantProxy
# so try to get the name of the target class
begin
target = constant.__send__(:target)
rescue NoMethodError
return
end

name_of(target)
end
end
end
end
57 changes: 57 additions & 0 deletions lib/tapioca/helpers/rbi_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module RBIHelper
extend T::Sig
include SorbetHelper
extend SorbetHelper
include Runtime::Reflection
extend self

class << self
Expand Down Expand Up @@ -138,5 +139,61 @@ def valid_parameter_name?(name)
rescue SyntaxError
false
end

sig { params(constant: T.all(Module, T::Generic)).returns(String) }
def generic_name_of(constant)
type_name = T.must(qualified_name_of(constant))
return type_name if type_name =~ /\[.*\]$/

type_variables = Runtime::GenericTypeRegistry.lookup_type_variables(constant)
return type_name unless type_variables

type_variables = type_variables.reject(&:fixed?)
return type_name if type_variables.empty?

type_variable_names = type_variables.map { "T.untyped" }.join(", ")

"#{type_name}[#{type_variable_names}]"
end

sig { params(constant: Module).returns(T.nilable(String)) }
def qualified_name_of(constant)
name = name_of(constant)
return if name.nil?

if name.start_with?("::")
name
else
"::#{name}"
end
end

sig { params(constant: Module).returns(T.nilable(String)) }
def name_of(constant)
name = name_of_proxy_target(constant, super(class_of(constant)))
return name if name

name = super(constant)
return if name.nil?
return unless are_equal?(constant, constantize(name, inherit: true))

name = "Struct" if name =~ /^(::)?Struct::[^:]+$/
name
end

sig { params(constant: Module, class_name: T.nilable(String)).returns(T.nilable(String)) }
def name_of_proxy_target(constant, class_name)
return unless class_name == "ActiveSupport::Deprecation::DeprecatedConstantProxy"

# We are dealing with a ActiveSupport::Deprecation::DeprecatedConstantProxy
# so try to get the name of the target class
begin
target = constant.__send__(:target)
rescue NoMethodError
return
end

name_of(target)
end
end
end
Loading

0 comments on commit 5c2ce79

Please sign in to comment.