module Tapioca::GenericTypeRegistry

This class is responsible for storing and looking up information related to generic types.

The class stores 2 different kinds of data, in two separate lookup tables:

1. a lookup of generic type instances by name: `@generic_instances`
2. a lookup of type variable serializer by constant and type variable
   instance: `@type_variables`

By storing the above data, we can cheaply query each constant against this registry to see if it declares any generic type variables. This becomes a simple lookup in the `@type_variables` hash table with the given constant.

If there is no entry, then we can cheaply know that we can skip generic type information generation for this type.

On the other hand, if we get a result, then the result will be a hash of type variable to type variable serializers. This allows us to associate type variables to the constant names that represent them, easily.

Constants

TypeVariable

Public Class Methods

lookup_type_variables(constant) click to toggle source
# File lib/tapioca/generic_type_registry.rb, line 64
def lookup_type_variables(constant)
  @type_variables[constant]
end
register_type(constant, types) click to toggle source
# File lib/tapioca/generic_type_registry.rb, line 49
def register_type(constant, types)
  # Build the name of the instantiated generic type,
  # something like `"Foo[X, Y, Z]"`
  type_list = types.map { |type| T::Utils.coerce(type).name }.join(", ")
  name = "#{Reflection.name_of(constant)}[#{type_list}]"

  # Create a generic type with an overridden `name`
  # method that returns the name we constructed above.
  #
  # Also, we try to memoize the generic type based on the name, so that
  # we don't have to keep recreating them all the time.
  @generic_instances[name] ||= create_generic_type(constant, name)
end
register_type_variable(constant, type_variable) click to toggle source
# File lib/tapioca/generic_type_registry.rb, line 83
def register_type_variable(constant, type_variable)
  type_variables = lookup_or_initialize_type_variables(constant)

  type_variables[type_variable] = type_variable.serialize
end

Private Class Methods

create_generic_type(constant, name) click to toggle source
# File lib/tapioca/generic_type_registry.rb, line 92
def create_generic_type(constant, name)
  generic_type = case constant
  when Class
    # For classes, we want to create a subclass, so that an instance of
    # the generic class `Foo[Bar]` is still a `Foo`. That is:
    # `Foo[Bar].new.is_a?(Foo)` should be true, which isn't the case
    # if we just clone the class. But subclassing works just fine.
    create_safe_subclass(constant)
  else
    # This can only be a module and it is fine to just clone modules
    # since they can't have instances and will not have `is_a?` relationships.
    # Moreover, we never `include`/`extend` any generic modules into the
    # ancestor tree, so this doesn't become a problem with checking the
    # instance of a class being `is_a?` of a module type.
    constant.clone
  end

  # Let's set the `name` method to return the proper generic name
  generic_type.define_singleton_method(:name) { name }

  # Return the generic type we created
  generic_type
end
create_safe_subclass(constant) click to toggle source
# File lib/tapioca/generic_type_registry.rb, line 117
def create_safe_subclass(constant)
  # Lookup the "inherited" class method
  inherited_method = constant.method(:inherited)
  # and the module that defines it
  owner = inherited_method.owner

  # If no one has overriden the inherited method yet, just subclass
  return Class.new(constant) if Class == owner

  begin
    # Otherwise, some inherited method could be preventing us
    # from creating subclasses, so let's override it and rescue
    owner.send(:define_method, :inherited) do |s|
      inherited_method.call(s)
    rescue
      # Ignoring errors
    end

    # return a subclass
    Class.new(constant)
  ensure
    # Reinstate the original inherited method back.
    owner.send(:define_method, :inherited, inherited_method)
  end
end
lookup_or_initialize_type_variables(constant) click to toggle source
# File lib/tapioca/generic_type_registry.rb, line 144
def lookup_or_initialize_type_variables(constant)
  @type_variables[constant] ||= {}.compare_by_identity
end