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