Add pre and post build operations

https://fedorahosted.org/freeipa/ticket/3235
This commit is contained in:
Petr Vobornik 2013-04-05 18:38:02 +02:00
parent 03d3742376
commit abdb5455d1
3 changed files with 190 additions and 31 deletions

View File

@ -41,17 +41,19 @@ define(['dojo/_base/declare',
/**
* Build object based on spec.
*
* @param spec {String|Function|Object} Build spec
* @param {String|Function|Object} Build spec
*
* String: type name, queries registry
* Function: factory or constructor
* Object: spec object
*
* Build control properies of spec object:
* constructor: Function
* factory: Function
* mixim_spec: Boolean
* type: String
* $constructor: Function
* $factory: Function
* $mixim_spec: Boolean
* $type: String
* $pre_ops: []
* $post_ops: []
*
* All other properties will be passed to object construction method.
*/
@ -78,16 +80,20 @@ define(['dojo/_base/declare',
// spec is type name
cs = this._query_registry(spec);
} else if (typeof spec === 'object') {
var c = spec.constructor,
f = spec.factory,
m = spec.mixim_spec,
t = spec.type;
var c = spec.$constructor,
f = spec.$factory,
m = spec.$mixim_spec,
t = spec.$type,
pre = spec.$pre_ops,
post = spec.$post_ops;
var s = lang.clone(spec);
delete s.constructor;
delete s.factory;
delete s.mixim_spec;
delete s.type;
delete s.$constructor;
delete s.$factory;
delete s.$mixim_spec;
delete s.$type;
delete s.$pre_ops;
delete s.$post_ops;
if (c) {
cs.constructor = c;
@ -105,19 +111,30 @@ define(['dojo/_base/declare',
cs.spec = s;
}
}
cs.pre_ops = cs.pre_ops || [];
cs.post_ops = cs.post_ops || [];
if (pre) cs.pre_ops.push.call(cs.pre_ops, pre);
if (pre) cs.post_ops.push.call(cs.post_ops, post);
}
return cs;
},
/**
* Queries registry and returns copy of construction specification
*/
_query_registry: function(type) {
if (this.registry) {
return this.registry.get(type);
var cs = this.registry.get(type);
if (!cs) throw construct.no_cs_for_type_error(type);
cs = construct.copy_cs(cs);
return cs;
} else {
throw {
error: 'Build error: construct registry required',
spec: type
builder: this
};
}
},
@ -125,7 +142,22 @@ define(['dojo/_base/declare',
_build: function(construction_spec) {
var cs = construction_spec,
obj = null;
obj = null,
i;
if (cs.pre_ops) {
for (i=0; i<cs.pre_ops.length; i++) {
var preop = cs.pre_ops[i];
var preop_t = typeof preop;
if (preop_t === 'function') {
cs.spec = preop(cs.spec || {});
} else if (preop_t === 'object') {
lang.mixin(cs.spec, preop);
}
}
}
cs.spec = cs.spec || {};
if (cs.factory && typeof cs.factory === 'function') {
obj = cs.factory(cs.spec);
@ -138,6 +170,18 @@ define(['dojo/_base/declare',
};
}
if (cs.post_ops && obj) {
for (i=0; i<cs.post_ops.length; i++) {
var postop = cs.post_ops[i];
var postop_t = typeof postop;
if (postop_t === 'function') {
obj = postop(obj);
} else if (postop_t === 'object') {
lang.mixin(obj, postop);
}
}
}
return obj;
},

View File

@ -50,10 +50,12 @@ define(['dojo/_base/declare',
*
* May be defined by single construction spec object:
* var construction_spec = {
* type: string,
* factory: function,
* constructor: function,
* spec: object
* type: String,
* factory: Function,
* constructor: Function,
* spec: Object,
* pre_ops: [],
* post_ops: []
* };
* register(construction_spec);
*
@ -62,13 +64,13 @@ define(['dojo/_base/declare',
*/
register: function(type, func, default_spec) {
var spec, f, c;
var cs, f, c;
if (typeof type === 'object') {
spec = type;
cs = type;
} else {
construct.is_constructor(func) ? c = func : f = func;
spec = {
cs = {
type: type,
factory: f,
constructor: c,
@ -76,16 +78,87 @@ define(['dojo/_base/declare',
};
}
if (typeof spec.type !== 'string' || spec.type !== '') {
throw 'Argument exception: Invalid type';
}
if (typeof spec.factory !== 'function' &&
typeof spec.constructor !== 'function') {
throw 'Argument exception: No factory or constructor defined';
if (!cs.pre_ops) cs.pre_ops = [];
if (!cs.post_ops) cs.post_ops = [];
this._check_spec(cs);
this._map[cs.type] = cs;
return cs;
},
/**
* Makes a copy of construct specification of original type. Extends
* it with values in supplied construct specification.
*
* @param {String} Original type
* @param {String} New type
* @param {Object} Construction specification
*/
copy: function(org_type, new_type, construct_spec) {
var def_cs = construct_spec;
var old_cs = this._check_get(org_type);
var cs = construct.copy_cs(old_cs);
cs.type = new_type;
if (def_cs.pre_ops) cs.pre_ops.push.call(cs.pre_ops, def_cs.pre_ops);
if (def_cs.post_ops) cs.post_ops.push.call(cs.post_ops, def_cs.post_ops);
if (def_cs.factory) cs.factory = def_cs.factory;
if (def_cs.constructor) cs.constructor = def_cs.constructor;
if (def_cs.spec) {
cs.spec = cs.spec || {};
lang.mixin(cs.spec, def_cs.spec);
}
this._map[spec.type] = spec;
return spec;
this._check_spec(cs);
this._map[cs.type] = cs;
return cs;
},
/**
* Registers pre operation.
*
* Purpose of pre operation is to modify spec object before build
* operation.
*
* When op is Function it gets called with spec as a param and should
* return modified spec.
*
* When op is Object, the object gets mixed in into spec.
*
* @param {type} type
* @param {Function|Object} op
* @param {Boolean} move op to first position
*/
register_pre_op: function(type, op, first) {
var cs = this._check_get(type);
if (first) cs.pre_ops.unshift(op);
else cs.pre_ops.push(op);
},
/**
* Registers post operation.
*
* Purpose of post operation is to modify built object.
*
* When op is Function it gets called with built object as a param
* and should return modified obj.
*
* When op is Object, the object gets mixed in into built object. Use
* with caution.
*
* @param {type} type
* @param {Function|Object} op
* @param {Boolean} move op to first position
*/
register_post_op: function(type, op, first) {
var cs = this._check_get(type);
if (first) cs.post_ops.unshift(op);
else cs.post_ops.push(op);
},
/**
@ -96,6 +169,28 @@ define(['dojo/_base/declare',
*/
get: function(type) {
return this._map[type] || null;
},
_check_get: function(type) {
var cs = this.get(type);
if (!cs) throw construct.no_cs_for_type_error(type);
return cs;
},
_check_spec: function(spec) {
if (typeof spec.type !== 'string' || spec.type === '') {
throw 'Argument exception: Invalid type';
}
if (typeof spec.factory !== 'function' &&
typeof spec.constructor !== 'function') {
throw 'Argument exception: No factory or constructor defined';
}
if (!lang.isArrayLike(spec.pre_ops)) {
throw 'Argument exception: Invalid pre_ops type.';
}
if (!lang.isArrayLike(spec.post_ops)) {
throw 'Argument exception: Invalid post_ops type.';
}
}
});

View File

@ -37,6 +37,26 @@ define(['dojo/_base/declare',
// TODO: Find better method. Check by extend might not be very
// reliable.
return typeof obj === 'function' && typeof obj.extend === 'function';
},
/**
* Creates copy of construction specification
*
* It makes sure that pre_ops, post_ops and spec are new Arrays/Object
*/
copy_cs: function(org_cs) {
var cs = lang.clone(org_cs);
if (cs.spec) cs.spec = lang.clone(cs.spec);
cs.pre_ops = cs.pre_ops.slice(0);
cs.post_ops = cs.pre_ops.slice(0);
return cs;
},
no_cs_for_type_error: function(type) {
return {
error: 'No construction specification for given type',
type: type
};
}
};
return construct;