1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
module Coercible
# A module that adds type lookup to a class
module TypeLookup
TYPE_FORMAT = /\A[A-Z]\w*\z/.freeze
# Set cache ivar on the model
#
# @param [Class] model
#
# @return [undefined]
#
# @api private
def self.extended(model)
model.instance_variable_set('@type_lookup_cache', {})
end
# Returns a descendant based on a name or class
#
# @example
# MyClass.determine_type('String') # => MyClass::String
#
# @param [Class, #to_s] class_or_name
# name of a class or a class itself
#
# @return [Class]
# a descendant
#
# @return [nil]
# nil if the type cannot be determined by the class_or_name
#
# @api public
def determine_type(class_or_name)
@type_lookup_cache[class_or_name] ||= determine_type_and_cache(class_or_name)
end
# Return the default primitive supported
#
# @return [Class]
#
# @api private
def primitive
raise NotImplementedError, "#{self}.primitive must be implemented"
end
private
# Determine type and cache the class
#
# @return [Class]
#
# @api private
def determine_type_and_cache(class_or_name)
case class_or_name
when singleton_class
determine_type_from_descendant(class_or_name)
when Class
determine_type_from_primitive(class_or_name)
else
determine_type_from_string(class_or_name.to_s)
end
end
# Return the class given a descendant
#
# @param [Class] descendant
#
# @return [Class]
#
# @api private
def determine_type_from_descendant(descendant)
descendant if descendant < self
end
# Return the class given a primitive
#
# @param [Class] primitive
#
# @return [Class]
#
# @return [nil]
# nil if the type cannot be determined by the primitive
#
# @api private
def determine_type_from_primitive(primitive)
type = nil
descendants.reverse_each do |descendant|
descendant_primitive = descendant.primitive
next unless primitive <= descendant_primitive
type = descendant if type.nil? or type.primitive > descendant_primitive
end
type
end
# Return the class given a string
#
# @param [String] string
#
# @return [Class]
#
# @return [nil]
# nil if the type cannot be determined by the string
#
# @api private
def determine_type_from_string(string)
if string =~ TYPE_FORMAT and const_defined?(string, *EXTRA_CONST_ARGS)
const_get(string, *EXTRA_CONST_ARGS)
end
end
end # module TypeLookup
end # module Virtus
|