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
//
// Feature(tag, suggested)
// -> feature
//
// Feature(tag, actions)
// -> feature
@ -337,6 +339,7 @@ var FeatureSetProto = {
// XXX add dependency loops to .conflicts...
// XXX might be a good idea to check dependency loops on feature
// construction, too... (???)
/*
buildFeatureList: function(obj, lst){
var that = this
obj = obj || {}
@ -666,8 +669,139 @@ var FeatureSetProto = {
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:
// - expand features:
@ -679,11 +813,12 @@ var FeatureSetProto = {
// - by priority
// - by dependency (detect loops/errors)
//
//
// XXX differences to .buildFeatureList(..):
// - order is slightly different -- within expectations...
// - this includes meta-features...
// this seems to be more logical and more flexible...
_buildFeatureList: function(lst, filter){
buildFeatureList: function(lst, filter){
var all = this.features
lst = (lst == null || lst == '*') ? all : 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 {
input: lst,
list: list,
features: list,
disabled: disabled,
excluded: excluded,
// XXX should these be in a error block???
missing: missing,
loops: loops,
conflicts: conflicts,
sort_loop_error: loop_limit <= 0,
// errors and conflicts...
error: (loops.length > 0
|| Object.keys(conflicts).length > 0
|| 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,
depended: rev_features,
//suggests: suggested,
@ -1128,18 +1253,34 @@ var FeatureSetProto = {
}
},
_setup: function(obj, lst){
// if no explicit object is given, just the list...
// Setup features...
//
// 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){
lst = obj
obj = null
}
obj = obj || (this.__actions__ || actions.Actions)()
lst = lst instanceof Array ? lst : [lst]
lst = lst.constructor !== Array ? [lst] : lst
var unapplicable = []
var features = this._buildFeatureList(lst,
var features = this.buildFeatureList(lst,
function(n){
var f = this[n]
// check applicability if possible...
@ -1149,55 +1290,44 @@ var FeatureSetProto = {
}
return true
})
lst = features.list
// XXX STUB: adapter code, remove when done...
features.unapplicable = unapplicable
features.features = features.list
// cleanup disabled...
features.disabled = features.disabled
.filter(function(n){ return unapplicable.indexOf(n) < 0 })
// check for conflicts...
/*/ XXX need to update this section...
if(Object.keys(features.conflicts).length != 0
|| Object.keys(features.missing).length != 0){
var m = features.missing
var c = features.conflicts
// if we have critical errors and set verbose...
var fatal = features.error
&& (features.error.loops.length > 0 || features.error.sort_loop_overflow)
var verbose = this.__verbose__ || fatal
// 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 stuff...
if(verbose){
// report dependency loops...
features.error.loops.length > 0
&& loops
.forEach(function(loop){
console.warn('feature loop detected:\n\t' + loop.join('\n\t\t-> ')) })
// report conflicts...
Object.keys(features.error.conflicts)
.forEach(function(group){
console.error('Exclusive "'+ group +'" conflict at:', conflicts[group]) })
// report loop limit...
features.error.sort_loop_overflow
&& console.error('Hit loop limit while sorting dependencies!')
}
// report excluded features...
if(this.__verbose__ && features.excluded.length > 0){
console.warn('Excluded features due to exclusivity conflict:',
features.excluded.join(', '))
}
obj.features = features
// report unapplicable features...
if(this.__verbose__ && features.unapplicable.length > 0){
console.log('Features not applicable in current context:',
features.unapplicable.join(', '))
// fatal error -- can't load...
// XXX should we throw an error here???
if(fatal){
return
}
//*/
// do the setup...
var that = this
var setup = FeatureProto.setup
lst.forEach(function(n){
features.features.forEach(function(n){
// setup...
if(that[n] != null){
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
},
remove: function(obj, lst){
@ -1296,7 +1349,6 @@ var FeatureSetProto = {
},
// shorthand for: Feature(<feature-set>, ...)
// XXX should this return this?
Feature: function(){
return this.__feature__.apply(null, [this].concat(args2array(arguments))) },
}
@ -1306,7 +1358,6 @@ var FeatureSet =
module.FeatureSet = object.makeConstructor('FeatureSet', FeatureSetProto)
//---------------------------------------------------------------------
var Features =

View File

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