moved to new feature inertization algorithm, still testing...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2017-06-22 16:09:19 +03:00
parent 2754cf5457
commit 6f553d2408
2 changed files with 204 additions and 153 deletions

View File

@ -170,6 +170,8 @@ module.FeatureProto = {
// Feature(tag, obj) // Feature(tag, obj)
// -> feature // -> feature
// //
// Feature(tag, suggested)
// -> feature
// //
// Feature(tag, actions) // Feature(tag, actions)
// -> feature // -> feature
@ -337,6 +339,7 @@ var FeatureSetProto = {
// XXX add dependency loops to .conflicts... // XXX add dependency loops to .conflicts...
// XXX might be a good idea to check dependency loops on feature // XXX might be a good idea to check dependency loops on feature
// construction, too... (???) // construction, too... (???)
/*
buildFeatureList: function(obj, lst){ buildFeatureList: function(obj, lst){
var that = this var that = this
obj = obj || {} obj = obj || {}
@ -666,8 +669,139 @@ var FeatureSetProto = {
conflicts: conflicts, conflicts: conflicts,
} }
}, },
setup: function(obj, lst){
// if no explicit object is given, just the list...
if(lst == null){
lst = obj
obj = null
}
// Build list of features in an appropriate order to load... obj = obj || (this.__actions__ || actions.Actions)()
lst = lst.constructor !== Array ? [lst] : lst
var features = this.buildFeatureList(obj, lst)
lst = features.features
// check for conflicts...
if(Object.keys(features.conflicts).length != 0
|| Object.keys(features.missing).length != 0){
var m = features.missing
var c = features.conflicts
// build a report...
var report = []
// missing deps...
Object.keys(m).forEach(function(k){
report.push(k + ': requires following missing features:\n'
+' ' + m[k].join(', '))
})
report.push('\n')
// conflicts...
Object.keys(c).forEach(function(k){
report.push(k + ': must setup after:\n ' + c[k].join(', '))
})
// break...
throw 'Feature dependency error:\n ' + report.join('\n ')
}
// report excluded features...
if(this.__verbose__ && features.excluded.length > 0){
console.warn('Excluded features due to exclusivity conflict:',
features.excluded.join(', '))
}
// report unapplicable features...
if(this.__verbose__ && features.unapplicable.length > 0){
console.log('Features not applicable in current context:',
features.unapplicable.join(', '))
}
// do the setup...
var that = this
var setup = FeatureProto.setup
lst.forEach(function(n){
// setup...
if(that[n] != null){
this.__verbose__ && console.log('Setting up feature:', n)
setup.call(that[n], obj)
}
})
// XXX should we extend this if it already was in the object???
obj.features = features
return obj
},
//*/
// Build list of features in load order...
//
// .buildFeatureList()
// .buildFeatureList('*')
// -> data
//
// .buildFeatureList(feature-tag)
// -> data
//
// .buildFeatureList([feature-tag, .. ])
// -> data
//
// .buildFeatureList(.., filter)
// -> data
//
//
// return format:
// {
// // input feature feature tags...
// input: [ .. ],
//
// // output list of feature tags...
// features: [ .. ],
//
// // disabled features...
// disabled: [ .. ],
// // exclusive features that got excluded...
// excluded: [ .. ],
//
// // Errors...
// error: null | {
// // fatal/recoverable error indicator...
// fatal: bool,
//
// // missing dependencies...
// // NOTE: this includes tags only included by .depends and
// // ignores tags from .suggested...
// missing: [ .. ],
//
// // XXX
// conflicts: conflicts,
//
// // detected dependency loops (if .length > 0 sets fatal)...
// loops: [ .. ],
//
// // sorting loop overflow error (if true sets fatal)...
// sort_loop_overflow: bool,
// },
//
// // Introspection...
// // index of features and their list of dependencies...
// depends: {
// feature-tag: [ feature-tag, .. ],
// ..
// },
// // index of features and list of features depending on them...
// // XXX should this include suggestions or should we do them
// // in a separate list...
// depended: {
// feature-tag: [ feature-tag, .. ],
// ..
// },
// }
//
// //
// Algorithm: // Algorithm:
// - expand features: // - expand features:
@ -679,11 +813,12 @@ var FeatureSetProto = {
// - by priority // - by priority
// - by dependency (detect loops/errors) // - by dependency (detect loops/errors)
// //
//
// XXX differences to .buildFeatureList(..): // XXX differences to .buildFeatureList(..):
// - order is slightly different -- within expectations... // - order is slightly different -- within expectations...
// - this includes meta-features... // - this includes meta-features...
// this seems to be more logical and more flexible... // this seems to be more logical and more flexible...
_buildFeatureList: function(lst, filter){ buildFeatureList: function(lst, filter){
var all = this.features var all = this.features
lst = (lst == null || lst == '*') ? all : lst lst = (lst == null || lst == '*') ? all : lst
lst = lst.constructor !== Array ? [lst] : lst lst = lst.constructor !== Array ? [lst] : lst
@ -1085,42 +1220,32 @@ var FeatureSetProto = {
//------------------------------------------------------------- //-------------------------------------------------------------
// XXX should we report stuff here???
// report dependency loops...
//
// NOTE: a loop error should be raised only when one of the loop elements
// is encountered during the linearisation process...
// XXX should looping features get disabled or be loaded in random-ish order???
// ...#2 case will need to be handled at the sorting stage...
loops.length > 0
&& loops
.forEach(function(loop){
console.warn('feature loop detected:\n\t' + loop.join('\n\t\t-> ')) })
// report conflicts...
Object.keys(conflicts)
.forEach(function(group){
console.error('Exclusive "'+ group +'" conflict at:', conflicts[group]) })
// report loop limit...
// XXX store this to data...
loop_limit <= 0
&& console.error('Hit loop limit while sorting dependencies!')
return { return {
input: lst, input: lst,
list: list, features: list,
disabled: disabled, disabled: disabled,
excluded: excluded, excluded: excluded,
// XXX should these be in a error block??? // errors and conflicts...
missing: missing, error: (loops.length > 0
loops: loops, || Object.keys(conflicts).length > 0
conflicts: conflicts, || loop_limit <= 0
sort_loop_error: loop_limit <= 0, || missing.length > 0) ?
{
missing: missing,
conflicts: conflicts,
// fatal stuff...
fatal: loops.length > 0 || loop_limit <= 0,
loops: loops,
sort_loop_overflow: loop_limit <= 0,
}
: null,
// introspection...
depends: features, depends: features,
depended: rev_features, depended: rev_features,
//suggests: suggested, //suggests: suggested,
@ -1128,18 +1253,34 @@ var FeatureSetProto = {
} }
}, },
_setup: function(obj, lst){ // Setup features...
// if no explicit object is given, just the list... //
// Setup features on existing actions object...
// .setup(actions, [feature-tag, ...])
// -> actions
//
// Setup features on a new actions object...
// .setup(feature-tag)
// .setup([feature-tag, ...])
// -> actions
//
//
// This will add .unapplicable to the output of .buildFeatureList(..)
// and to .features of the resulting object...
//
// NOTE: this will store the build result in .features of the output
// actions object.
setup: function(obj, lst){
// no explicit object is given...
if(lst == null){ if(lst == null){
lst = obj lst = obj
obj = null obj = null
} }
obj = obj || (this.__actions__ || actions.Actions)() obj = obj || (this.__actions__ || actions.Actions)()
lst = lst instanceof Array ? lst : [lst]
lst = lst.constructor !== Array ? [lst] : lst
var unapplicable = [] var unapplicable = []
var features = this._buildFeatureList(lst, var features = this.buildFeatureList(lst,
function(n){ function(n){
var f = this[n] var f = this[n]
// check applicability if possible... // check applicability if possible...
@ -1149,55 +1290,44 @@ var FeatureSetProto = {
} }
return true return true
}) })
lst = features.list
// XXX STUB: adapter code, remove when done...
features.unapplicable = unapplicable features.unapplicable = unapplicable
features.features = features.list // cleanup disabled...
features.disabled = features.disabled
.filter(function(n){ return unapplicable.indexOf(n) < 0 })
// check for conflicts... // if we have critical errors and set verbose...
/*/ XXX need to update this section... var fatal = features.error
if(Object.keys(features.conflicts).length != 0 && (features.error.loops.length > 0 || features.error.sort_loop_overflow)
|| Object.keys(features.missing).length != 0){ var verbose = this.__verbose__ || fatal
var m = features.missing
var c = features.conflicts
// build a report... // report stuff...
var report = [] if(verbose){
// report dependency loops...
// missing deps... features.error.loops.length > 0
Object.keys(m).forEach(function(k){ && loops
report.push(k + ': requires following missing features:\n' .forEach(function(loop){
+' ' + m[k].join(', ')) console.warn('feature loop detected:\n\t' + loop.join('\n\t\t-> ')) })
}) // report conflicts...
report.push('\n') Object.keys(features.error.conflicts)
.forEach(function(group){
// conflicts... console.error('Exclusive "'+ group +'" conflict at:', conflicts[group]) })
Object.keys(c).forEach(function(k){ // report loop limit...
report.push(k + ': must setup after:\n ' + c[k].join(', ')) features.error.sort_loop_overflow
}) && console.error('Hit loop limit while sorting dependencies!')
// break...
throw 'Feature dependency error:\n ' + report.join('\n ')
} }
// report excluded features... obj.features = features
if(this.__verbose__ && features.excluded.length > 0){
console.warn('Excluded features due to exclusivity conflict:',
features.excluded.join(', '))
}
// report unapplicable features... // fatal error -- can't load...
if(this.__verbose__ && features.unapplicable.length > 0){ // XXX should we throw an error here???
console.log('Features not applicable in current context:', if(fatal){
features.unapplicable.join(', ')) return
} }
//*/
// do the setup... // do the setup...
var that = this var that = this
var setup = FeatureProto.setup var setup = FeatureProto.setup
lst.forEach(function(n){ features.features.forEach(function(n){
// setup... // setup...
if(that[n] != null){ if(that[n] != null){
this.__verbose__ && console.log('Setting up feature:', n) this.__verbose__ && console.log('Setting up feature:', n)
@ -1205,83 +1335,6 @@ var FeatureSetProto = {
} }
}) })
// XXX should we extend this if it already was in the object???
obj.features = features
return obj
},
//
// .setup(<actions>, [<feature>, ...])
// -> <actions>
//
// .setup([<feature>, ...])
// -> <actions>
//
setup: function(obj, lst){
// if no explicit object is given, just the list...
if(lst == null){
lst = obj
obj = null
}
obj = obj || (this.__actions__ || actions.Actions)()
lst = lst.constructor !== Array ? [lst] : lst
var features = this.buildFeatureList(obj, lst)
lst = features.features
// check for conflicts...
if(Object.keys(features.conflicts).length != 0
|| Object.keys(features.missing).length != 0){
var m = features.missing
var c = features.conflicts
// build a report...
var report = []
// missing deps...
Object.keys(m).forEach(function(k){
report.push(k + ': requires following missing features:\n'
+' ' + m[k].join(', '))
})
report.push('\n')
// conflicts...
Object.keys(c).forEach(function(k){
report.push(k + ': must setup after:\n ' + c[k].join(', '))
})
// break...
throw 'Feature dependency error:\n ' + report.join('\n ')
}
// report excluded features...
if(this.__verbose__ && features.excluded.length > 0){
console.warn('Excluded features due to exclusivity conflict:',
features.excluded.join(', '))
}
// report unapplicable features...
if(this.__verbose__ && features.unapplicable.length > 0){
console.log('Features not applicable in current context:',
features.unapplicable.join(', '))
}
// do the setup...
var that = this
var setup = FeatureProto.setup
lst.forEach(function(n){
// setup...
if(that[n] != null){
this.__verbose__ && console.log('Setting up feature:', n)
setup.call(that[n], obj)
}
})
// XXX should we extend this if it already was in the object???
obj.features = features
return obj return obj
}, },
remove: function(obj, lst){ remove: function(obj, lst){
@ -1296,7 +1349,6 @@ var FeatureSetProto = {
}, },
// shorthand for: Feature(<feature-set>, ...) // shorthand for: Feature(<feature-set>, ...)
// XXX should this return this?
Feature: function(){ Feature: function(){
return this.__feature__.apply(null, [this].concat(args2array(arguments))) }, return this.__feature__.apply(null, [this].concat(args2array(arguments))) },
} }
@ -1306,7 +1358,6 @@ var FeatureSet =
module.FeatureSet = object.makeConstructor('FeatureSet', FeatureSetProto) module.FeatureSet = object.makeConstructor('FeatureSet', FeatureSetProto)
//--------------------------------------------------------------------- //---------------------------------------------------------------------
var Features = var Features =

View File

@ -1,6 +1,6 @@
{ {
"name": "ig-features", "name": "ig-features",
"version": "2.2.6", "version": "3.0.0",
"description": "", "description": "",
"main": "features.js", "main": "features.js",
"scripts": { "scripts": {