UNPKG

abstract-type

Version:

The abstract-type library includes the abstract `Type` class and `Value` class for streamable type info and validating value.

294 lines (277 loc) 10.4 kB
properties = require 'property-manager/ability' factory = require 'custom-factory' createObject = require 'inherits-ex/lib/createObject' isInheritedFrom = require 'inherits-ex/lib/isInheritedFrom' isObject = require 'util-ex/lib/is/type/object' isFunction = require 'util-ex/lib/is/type/function' isString = require 'util-ex/lib/is/type/string' isArray = require 'util-ex/lib/is/type/array' isUndefined = require 'util-ex/lib/is/type/undefined' defineProperty = require 'util-ex/lib/defineProperty' extend = require 'util-ex/lib/_extend' Attributes = require './attributes/' attributes = new Attributes() objectToString = Object::toString getObjectKeys = Object.keys class Value @tryGetTypeName: (aValue)-> if aValue instanceof Object result = objectToString.call aValue i = result.lastIndexOf ' ' result = result.substring(i+1, result.length-1) if i >= 0 else result = typeof aValue result =result.charAt(0).toUpperCase() + result.substring(1) result constructor: (aValue, aType, aOptions)-> if not (aType instanceof Type) aOptions = aType vTypeName = Value.tryGetTypeName(aValue) if aOptions # TODO: whether cache this type? aType = Type.create vTypeName, aOptions else aType = Type vTypeName throw new TypeError 'can not determine the value type.' unless aType if not (this instanceof Value) return createObject aType.ValueType, aValue, aType, aOptions defineProperty @, '$type', aType @_initialize(aValue, aType, aOptions) @assign(aValue, aOptions) isValid: (aOptions)->@$type.isValid(@valueOf(), aOptions) _initialize: (aValue, aType, aOptions)-> defineProperty @, 'value', null _assign:(aValue)-> @value = aValue return assign: (aValue, aOptions)-> checkValidity = aOptions.checkValidity if aOptions vType = @$type if isFunction vType.toValue aValue = vType.toValue aValue, aOptions else if aValue instanceof Value aValue = aValue.valueOf() @$type.validate(aValue, checkValidity) if checkValidity isnt false @_assign aValue @ create: (aValue, aOptions)-> @$type.createValue aValue, aOptions clone: (aOptions) -> @create @valueOf(), aOptions toString: -> String(@valueOf()) valueOf: ->@value _toObject: (aOptions)->@valueOf() toObject: (aOptions)-> if aOptions and aOptions.withType delete aOptions.withType result = @toObjectWithType(aOptions) else result = @_toObject(aOptions) result toObjectWithType: (aOptions)-> value = @_toObject(aOptions) #aOptions = {} unless aOptions result = @$type.toObject(aOptions) result.value = value result # assign value from JSON string. fromJson: (aString)-> aString = JSON.parse aString @assign aString # create a new value object from JSON string. createFromJson: (aString)-> aString = JSON.parse aString vType = @$type aString = vType.toValue aString if vType.toValue createObject @$type.ValueType, aString, vType toJSON: (aOptions)-> result = @toObject(aOptions) vType = @$type if vType.valueToString if result.hasOwnProperty 'value' result.value = vType.valueToString result.value else result = vType.valueToString result result # convert a Json string. toJson: (aOptions)-> result = @toJSON(aOptions) JSON.stringify result inspect: -> '<type '+@$type._inspect(value:@_toObject())+'>' #End Value class module.exports = class Type factory Type properties Type, name: 'advance' @ROOT_NAME: 'type' # export the Value Class from here @Value: Value # override for inherited type class: ValueType: Value $attributes: attributes constructor: (aTypeName, aOptions)-> # create a new instance object if aOptions is not the original # options of the type. if not (this instanceof Type) and not (aTypeName instanceof Type) if aTypeName if isObject aTypeName aOptions = aTypeName aTypeName = attributes.getValue aOptions, 'name' else if not isString aTypeName aOptions = aTypeName aTypeName = undefined if not aTypeName # arguments.callee is forbidden if strict mode enabled. try vCaller = arguments.callee.caller if vCaller and isInheritedFrom vCaller, Type aTypeName = vCaller vCaller = vCaller.caller #get farest hierarchical registered class while isInheritedFrom vCaller, aTypeName aTypeName = vCaller vCaller = vCaller.caller aTypeName = Type.getNameFromClass(aTypeName) if aTypeName return unless aTypeName vTypeClass = Type.registeredClass aTypeName if vTypeClass and aOptions and not vTypeClass::$attributes.isDefaultObject(aOptions) return createObject vTypeClass, aOptions return super initialize: (aOptions)-> defineProperty @, 'errors', null @$attributes.initializeTo @ if @$attributes @_initialize aOptions if @_initialize @assign(aOptions) if aOptions? finalize: (aOptions)-> @errors = null if @errors @_finalize(aOptions) if @_finalize @validators: {} # aValidatorFn = (aValue, aOptions)-> @registerValidator: (aValidator, aValidatorFn)-> vIsFn = null if isObject aValidator aValidatorFn = aValidator.validate if vIsFn = isFunction aValidator.validate aValidator = aValidator.name if aValidator.name vIsFn = isFunction(aValidatorFn) if vIsFn is null result = vIsFn and isString(aValidator) and !Type.validators[aValidator] Type.validators[aValidator] = aValidatorFn if result return result @unregisterValidator: (aValidatorName)-> delete Type.validators[aValidatorName] _checkValidator: (aValue, aOptions)-> aOptions = @ unless isObject aOptions result = true for vName, vFn of Type.validators if aOptions[vName]? or @$attributes.getRealAttrName vName # u can push errors in the validation function result = vFn.call @, aValue, aOptions break unless result result oldAssign = @::assign assign: (aOptions, aExclude)-> @errors = [] return oldAssign.call @, aOptions, aExclude _validate: (aValue, aOptions)->true error: (aMessage, aOptions)-> name = (aOptions && @$attributes.getValue(aOptions, 'name')) || String(@) @errors.push name: name, message: aMessage return isRequired: (aValue, aOptions)-> aOptions = @ unless isObject aOptions vRequired = @$attributes.getValue aOptions, 'required' result = not vRequired or (vRequired is true and aValue?) validateRequired: (aValue, aOptions)-> result = @isRequired aValue, aOptions @error 'is required', aOptions unless result result validate: (aValue, raiseError, aOptions)-> @errors = [] if isObject raiseError aOptions = raiseError raiseError = aOptions.raiseError customValidate = @$attributes.getValue(aOptions, 'customValidate') if aOptions aOptions = @mergeTo(aOptions, 'name') customValidate = aOptions.customValidate unless customValidate aOptions.raiseError = true if raiseError result = @validateRequired aValue, aOptions result = @_checkValidator(aValue, aOptions) if result result = customValidate.call(@, aValue, aOptions) if result and isFunction customValidate result = @_validate(aValue, aOptions) if result and aValue? if raiseError isnt false and not result throw new TypeError('"'+aValue + '" is an invalid ' + @name) result isValid: (aValue, aOptions) -> @validate(aValue, false, aOptions) createValue: (aValue, aOptions)-> if aOptions and not @isSame(aOptions) aOptions = @mergeTo(aOptions, 'name') # TODO: seperate the cache-able ability if isFunction Type.getCacheItem # this Type Factory is cache-able. aOptions.cached = true unless aOptions.cached? vType = Factory.getCacheItem @Class, aOptions else vType = @createType aOptions else vType = @ createObject vType.ValueType, aValue, vType, aOptions create: @::createValue createType: (aOptions)-> delete aOptions.value if aOptions result = createObject @Class, aOptions result cloneType: (aOptions)-> aOptions = @exportTo(aOptions, 'name', true, true) aOptions.name = @name unless aOptions.name @createType aOptions clone: @::cloneType # Get(create) a global Type class or create new Value from the json string. # it will create a new type object if options is not the original type # options. @fromJson: (aString)-> #aString = JSON.parse aString Type.from JSON.parse(aString) # create a new Type instance or create new Value from json string. @createFromJson: (aString)-> Type.createFrom JSON.parse aString # Get(create) a global Type class or create new Value from the parametric # type object. # it will create a new type object if options is not the original(default) # type options. @from: (aObject) -> result = Type aObject if aObject.value? and result result = result.createValue aObject.value result @createFrom: (aObject)-> value = aObject.value result = Type.create aObject.name, aObject result = result.createValue value if value? and result result toString: (aOptions)-> '[type '+ @name+']' _inspect: (aOptions, aNameRequired = false)-> result = '"' + @name + '"' vAttrs = @toJson(aOptions, aNameRequired).slice(1,-1) result += ': ' + vAttrs if vAttrs result inspect: -> '<type ' + @_inspect()+ '>' toJson: (aOptions, aNameRequired)-> result = @toObject(aOptions, aNameRequired) result = JSON.stringify result result _toObject:(aOptions, aNameRequired = true)-> vExclude = 'name' unless aNameRequired result = @exportTo aOptions, vExclude, true, true result toObject: (aOptions, aNameRequired)-> if aOptions unless aOptions.typeOnly or isUndefined aOptions.value value = aOptions.value aOptions = extend({}, aOptions) delete aOptions.typeOnly delete aOptions.value result = @_toObject(aOptions, aNameRequired) result.value = value unless isUndefined value result