gems.virtus-1.0.5.lib.virtus.attribute.builder.rb Maven / Gradle / Ivy
The newest version!
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?('::')
# TODO: wrap it up in Virtus.constantize and use feature-detection to
# pick up either Inflecto or ActiveSupport, whateve is available
if defined?(Inflecto)
Inflecto.constantize(type)
else
raise NotImplementedError, 'Virtus needs inflecto gem to constantize namespaced constant names'
end
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