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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
module Virtus
# Attribute placeholder used when type constant is passed as a string or symbol
#
# @private
class PendingAttribute
attr_reader :type, :options, :name
# @api private
def initialize(type, options)
@type, @options = type.to_s, options
@name = options[:name]
end
# @api private
def finalize
Attribute::Builder.call(determine_type, options).finalize
end
# @api private
def finalized?
false
end
# @api private
def determine_type
if type.include?('::')
Virtus.constantize(type)
else
Object.const_get(type)
end
end
end # PendingAttribute
# Extracts the actual type primitive from input type
#
# @private
class TypeDefinition
attr_reader :type, :primitive
# @api private
def initialize(type)
@type = type
initialize_primitive
end
# @api private
def pending?
@pending if defined?(@pending)
end
private
# @api private
def initialize_primitive
@primitive =
if type.instance_of?(String) || type.instance_of?(Symbol)
if !type.to_s.include?('::') && Object.const_defined?(type)
Object.const_get(type)
elsif not Attribute::Builder.determine_type(type)
@pending = true
type
else
type
end
elsif not type.is_a?(Class)
type.class
else
type
end
end
end
class Attribute
# Builder is used to set up an attribute instance based on input type and options
#
# @private
class Builder
attr_reader :attribute, :options, :type_definition, :klass, :type
# @api private
def self.call(type, options = {})
type_definition = TypeDefinition.new(type)
if type_definition.pending?
PendingAttribute.new(type, options)
else
new(type_definition, options).attribute
end
end
# @api private
def self.determine_type(klass, default = nil)
type = Attribute.determine_type(klass)
if klass.is_a?(Class)
type ||=
if klass < Axiom::Types::Type
determine_type(klass.primitive)
elsif EmbeddedValue.handles?(klass)
EmbeddedValue
elsif klass < Enumerable && !(klass <= Range)
Collection
end
end
type || default
end
# @api private
def initialize(type_definition, options)
@type_definition = type_definition
initialize_class
initialize_type
initialize_options(options)
initialize_default_value
initialize_coercer
initialize_attribute
end
private
# @api private
def initialize_class
@klass = self.class.determine_type(type_definition.primitive, Attribute)
end
# @api private
def initialize_type
@type = klass.build_type(type_definition)
end
# @api private
def initialize_options(options)
@options = klass.options.merge(:coerce => Virtus.coerce).update(options)
klass.merge_options!(type, @options)
determine_visibility
end
# @api private
def initialize_default_value
options.update(:default_value => DefaultValue.build(options[:default]))
end
# @api private
def initialize_coercer
options.update(:coercer => determine_coercer)
end
# @api private
def initialize_attribute
@attribute = klass.new(type, options)
@attribute.extend(Accessor) if options[:name]
@attribute.extend(Coercible) if options[:coerce]
@attribute.extend(NullifyBlank) if options[:nullify_blank]
@attribute.extend(Strict) if options[:strict]
@attribute.extend(LazyDefault) if options[:lazy]
@attribute.finalize if options[:finalize]
end
# @api private
def determine_coercer
options.fetch(:coercer) { klass.build_coercer(type, options) }
end
# @api private
def determine_visibility
default_accessor = options.fetch(:accessor)
reader_visibility = options.fetch(:reader, default_accessor)
writer_visibility = options.fetch(:writer, default_accessor)
options.update(:reader => reader_visibility, :writer => writer_visibility)
end
end # class Builder
end # class Attribute
end # module Virtus
|