lib/virtus/attribute/builder.rb in virtus-1.0.0.beta7 vs lib/virtus/attribute/builder.rb in virtus-1.0.0.beta8
- old
+ new
@@ -1,6 +1,81 @@
module Virtus
+
+ # @private
+ class PendingAttribute
+ attr_reader :name
+
+ # @api private
+ def initialize(type, options)
+ @type = type
+ @options = 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
+
+ class TypeDefinition
+ attr_reader :type, :primitive
+
+ # @api private
+ def initialize(type)
+ @type = type
+ initialize_primitive
+ end
+
+ # @api private
+ def pending?
+ @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
# TODO: this is a huge class and it might be a good idea to split it into
# smaller chunks. We probably need some option parser with dedicated
# sub-classes per attribute type (different one for Hash, Collection, EV)
@@ -8,12 +83,18 @@
# @private
class Builder
attr_reader :attribute
# @api private
- def self.call(*args)
- new(*args).attribute
+ 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)
@@ -31,66 +112,31 @@
type || default
end
# @api private
- def initialize(type, options)
- initialize_primitive(type)
+ def initialize(type_definition, options)
+ @type_definition = type_definition
+
initialize_class
- initialize_type(:type => type, :primitive => @primitive)
+ initialize_type
initialize_options(options)
initialize_default_value
initialize_coercer
initialize_attribute
end
private
# @api private
- def initialize_attribute
- @attribute = @klass.new(@type, @options) do |attribute|
- attribute.extend(Accessor) if @options[:name]
- attribute.extend(Coercible) if @options[:coerce]
- attribute.extend(Strict) if @options[:strict]
- attribute.extend(LazyDefault) if @options[:lazy]
- end
- end
-
- # @api private
- def initialize_default_value
- @options.update(:default_value => DefaultValue.build(@options[:default]))
- end
-
- # @api private
- def initialize_coercer
- @options.update(:coercer => @options.fetch(:coercer) { @klass.build_coercer(@type, @options) })
- end
-
- # @api private
- def initialize_primitive(type)
- @primitive =
- if type.instance_of?(String) || type.instance_of?(Symbol)
- begin
- Object.const_get(type)
- rescue
- type
- end
- elsif not type.is_a?(Class)
- type.class
- else
- type
- end
- end
-
- # @api private
def initialize_class
- @klass = self.class.determine_type(@primitive, Attribute)
+ @klass = self.class.determine_type(@type_definition.primitive, Attribute)
end
# @api private
- def initialize_type(options)
- @type = @klass.build_type(options)
+ 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)
@@ -102,9 +148,30 @@
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
+
+ # @api private
+ def initialize_default_value
+ @options.update(:default_value => DefaultValue.build(@options[:default]))
+ end
+
+ # @api private
+ def initialize_coercer
+ @options.update(:coercer => @options.fetch(:coercer) { @klass.build_coercer(@type, @options) })
+ end
+
+ # @api private
+ def initialize_attribute
+ @attribute = @klass.new(@type, @options) do |attribute|
+ attribute.extend(Accessor) if @options[:name]
+ attribute.extend(Coercible) if @options[:coerce]
+ attribute.extend(Strict) if @options[:strict]
+ attribute.extend(LazyDefault) if @options[:lazy]
+ end
+ @attribute.finalize if @options[:finalize]
end
end # class Builder
end # class Attribute