# File lib/dm-core/property.rb, line 530
    def initialize(model, name, type, options = {})
      assert_kind_of 'model', model, Model
      assert_kind_of 'name',  name,  Symbol
      assert_kind_of 'type',  type,  Class

      if Fixnum == type
        # It was decided that Integer is a more expressively names class to
        # use instead of Fixnum.  Fixnum only represents smaller numbers,
        # so there was some confusion over whether or not it would also
        # work with Bignum too (it will).  Any Integer, which includes
        # Fixnum and Bignum, can be stored in this property.
        warn "#{type} properties are deprecated.  Please use Integer instead"
        type = Integer
      end

      unless TYPES.include?(type) || (DataMapper::Type > type && TYPES.include?(type.primitive))
        raise ArgumentError, "+type+ was #{type.inspect}, which is not a supported type: #{TYPES * ', '}", caller
      end

      @extra_options = {}
      (options.keys - PROPERTY_OPTIONS).each do |key|
        @extra_options[key] = options.delete(key)
      end

      @model                  = model
      @name                   = name.to_s.sub(/\?$/, '').to_sym
      @type                   = type
      @custom                 = DataMapper::Type > @type
      @options                = @custom ? @type.options.merge(options) : options
      @instance_variable_name = "@#{@name}"

      # TODO: This default should move to a DataMapper::Types::Text
      # Custom-Type and out of Property.
      @primitive = @options.fetch(:primitive, @type.respond_to?(:primitive) ? @type.primitive : @type)

      @getter       = TrueClass == @primitive ? "#{@name}?".to_sym : @name
      @field        = @options.fetch(:field,        nil)
      @serial       = @options.fetch(:serial,       false)
      @key          = @options.fetch(:key,          @serial || false)
      @default      = @options.fetch(:default,      nil)
      @nullable     = @options.fetch(:nullable,     @key == false)
      @index        = @options.fetch(:index,        false)
      @unique_index = @options.fetch(:unique_index, false)
      @lazy         = @options.fetch(:lazy,         @type.respond_to?(:lazy) ? @type.lazy : false) && !@key
      @fields       = {}

      @track = @options.fetch(:track) do
        if @custom && @type.respond_to?(:track) && @type.track
          @type.track
        else
          IMMUTABLE_TYPES.include?(@primitive) ? :set : :get
        end
      end

      # assign attributes per-type
      if String == @primitive || Class == @primitive
        @length = @options.fetch(:length, @options.fetch(:size, DEFAULT_LENGTH))
      elsif BigDecimal == @primitive || Float == @primitive
        @precision = @options.fetch(:precision, DEFAULT_PRECISION)

        default_scale = (Float == @primitive) ? DEFAULT_SCALE_FLOAT : DEFAULT_SCALE_BIGDECIMAL
        @scale     = @options.fetch(:scale, default_scale)
        # @scale     = @options.fetch(:scale, DEFAULT_SCALE_BIGDECIMAL)

        unless @precision > 0
          raise ArgumentError, "precision must be greater than 0, but was #{@precision.inspect}"
        end

        if (BigDecimal == @primitive) || (Float == @primitive && !@scale.nil?)
          unless @scale >= 0
            raise ArgumentError, "scale must be equal to or greater than 0, but was #{@scale.inspect}"
          end

          unless @precision >= @scale
            raise ArgumentError, "precision must be equal to or greater than scale, but was #{@precision.inspect} and scale was #{@scale.inspect}"
          end
        end
      end

      determine_visibility

      @model.auto_generate_validations(self)    if @model.respond_to?(:auto_generate_validations)
      @model.property_serialization_setup(self) if @model.respond_to?(:property_serialization_setup)
    end