Last active
August 29, 2015 14:22
-
-
Save kerbyfc/9d7a41c0288b6fa06f8b to your computer and use it in GitHub Desktop.
Blender.js (alfa) - expressed to resolve complex state consistency
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
( | |
(root, factory) -> | |
if typeof define is 'function' and define.amd | |
# AMD. Register as an anonymous module. | |
define [], factory | |
else if typeof exports is 'object' | |
# Node. Does not work with strict CommonJS, but | |
# only CommonJS-like environments that support module.exports, | |
# like Node. | |
module.exports = factory() | |
else | |
# Browser globals (root is window) | |
root.Blender = factory() | |
) this, -> | |
class Blender | |
###* | |
* Blender instancies | |
* @type { Array } | |
### | |
states = [] | |
###* | |
* Instance counter | |
* @type { Number } | |
### | |
couter = 0 | |
###* | |
* Rule types dict represents | |
* state consistency decision modifier | |
* @type { Object } | |
### | |
ruleTypes: | |
consistent: true | |
inconsistent: false | |
###* | |
* Methods that should be used as proxy | |
* @type {Array} | |
### | |
interface: [ | |
"blend" | |
"isConsistent" | |
"isntConsistent" | |
] | |
###* | |
* Setup blender instance | |
* @param { Object } bootstrap | |
### | |
constructor: (bootstrap) -> | |
@rules = {} | |
@data = {} | |
@_instance = couter++ | |
states[@_instance] = {} | |
for ruleType in Object.keys @ruleTypes | |
# default | |
@rules[ruleType] = [] | |
# check passed config | |
if typeof bootstrap[ruleType] is "object" | |
@rules[ruleType] = Array.prototype.map.call bootstrap[ruleType], (rule) -> | |
unless rule instanceof RegExp | |
return new RegExp rule | |
rule | |
# cut rules | |
delete bootstrap[ruleType] | |
# all other properties represents state | |
@options = bootstrap | |
###* | |
* @example | |
* if state.set({...}).consistent | |
* model.set state.get() | |
* | |
* @param { Object } data - new data for state | |
* @param { Object } options = force: false | |
### | |
set: (data, options = force: false) -> | |
mix = @blend data | |
# do not change state for safe mode | |
unless options.force or mix.consistent | |
return mix | |
states[@_instance] = mix | |
mix | |
###* | |
* Get copy of state | |
* @param { String } key | |
* @return { Any } data | |
### | |
get: (key) -> | |
state = {} | |
state[key] = val for key, val of states[@_instance] | |
unless key | |
state.data | |
else | |
state.data[key] | |
###* | |
* Blend new mix and check it's consistence | |
* @param { Object } data ingredients | |
* @return { Object } state | |
### | |
blend: (data) -> | |
state = | |
consistent : false | |
data : data | |
inconsistency : [] | |
positive = true | |
inconsistency = state.inconsistency | |
for type, mod of @ruleTypes | |
for rule in @rules[type] | |
# accumulate matching data | |
report = | |
options : [] | |
values : [] | |
summary : {} | |
type : type | |
rule : rule.toString() | |
# search matches for each data value | |
for key, value of state.data | |
if rule.test value | |
report.options.push key | |
report.values.push value | |
report.summary[key] = value | |
# if there are few options matched | |
# state becomes inconsistent for proper matching type | |
found = report.values.length > 1 | |
# by default state is consistent, | |
# positive rules are the first and if | |
# such rule was mached, we mark | |
# state as consistent and break | |
# positive rules mathing cycle | |
if mod is positive | |
if found | |
state.consistent = true | |
# break positive rules mathing cycle | |
break | |
else | |
# negative matches are after positive | |
if found | |
inconsistency.push report | |
state.consistent = false | |
unless state.inconsistency.length | |
delete state.inconsistency | |
state | |
###* | |
* Determine if data is consistent | |
* @param { Object } data ingredients | |
* @return { Boolean } consistent decision | |
### | |
isConsistent: (data = @get().data) -> | |
@blend data | |
.consistent | |
###* | |
* Determine if data is NOT consistent | |
* @param { Object } data ingredients | |
* @return { Boolean } consistent decision | |
### | |
isntConsistent: (data = @get().data) -> | |
not @blend data | |
.consistent | |
###* | |
* Create proxy methods for target object | |
* @param {Object} target | |
* @return {Object} target with blender methods | |
### | |
bridge: (target) -> | |
for method in @interface | |
do (method) => | |
unless target[method] | |
target[method] = => | |
@[method] arguments... | |
target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe "Blender", -> | |
before -> | |
@process = (data) -> | |
state = @blender.blend(data) | |
console.log JSON.stringify state, null, 4 | |
state | |
@blender = new Blender | |
consistent: [ | |
# show "all" violation level option only for line view | |
/// | |
line # view | |
|Levels.all # option | |
/// | |
# show some options only with proper view types | |
/// | |
line # view | |
|column_stacked # view | |
|person_activity # type | |
|count | |
|groupingByPeriod # option | |
|Levels.(high|low|medium|no) # option(s) | |
/// | |
# show some options only for non-dynamic charts | |
/// | |
showOthersGroup # option | |
|showValues # option | |
|showPercentage # option | |
|bar # view(s) | |
|pie # view | |
/// | |
/// | |
bar # view | |
|pie # view | |
|object_type_code # type | |
|category # type | |
|protected_document # type | |
|protected_catalog # type | |
|object_to_list # type | |
|policy # type | |
|sender_receiver # type | |
|sender # type | |
|receiver # type | |
|workstation # type | |
|web_resources # type | |
|user_decision # type | |
/// | |
] | |
inconsistent: [ | |
# disable bar & pie for charts that represents dynamic | |
/// | |
person_activity # type | |
|bar # view | |
|pie # view | |
/// | |
# dont show some options for column & line | |
/// | |
column_stacked # view | |
|line # view | |
|limit # option | |
|showValues # option | |
/// | |
# dont show limit for some types | |
/// | |
limit # option | |
|object_type_code # type | |
|user_decision # type | |
/// | |
] | |
it "1. should determine state inconsistence", -> | |
@process | |
type: "protected_document" | |
view: "bar_grouped" | |
.consistent.should.be.ok | |
it "2. should determine state inconsistence", -> | |
consistence = @process type: "person_activity", view: "bar_grouped" | |
consistence.consistent.should.not.be.ok | |
consistence.should.be.eql({ | |
"consistent": false, | |
"data": { | |
"type": "person_activity", | |
"view": "bar_grouped" | |
}, | |
"inconsistency": [ | |
{ | |
"options": [ | |
"type", | |
"view" | |
], | |
"values": [ | |
"person_activity", | |
"bar_grouped" | |
], | |
"summary": { | |
"type": "person_activity", | |
"view": "bar_grouped" | |
}, | |
"type": "inconsistent", | |
"rule": "/person_activity|bar|pie/" | |
} | |
] | |
}) | |
it "3. should determine state inconsistence", -> | |
@process type: "person_activity", view: "line" | |
.consistent.should.be.ok | |
consistence = @process type: "protected_document", view: "column_stacked" | |
consistence.consistent.should.not.be.ok | |
consistence.should.be.eql({ | |
"consistent": false, | |
"data": { | |
"type": "protected_document", | |
"view": "column_stacked" | |
} | |
}) | |
it "should determine state consistence", -> | |
consistence = @process | |
type: "person_activity" | |
view: "line" | |
option: "count" | |
consistence.should.eql({ | |
"consistent": true, | |
"data": { | |
"type": "person_activity", | |
"view": "line", | |
"option": "count" | |
} | |
}) | |
consistence.consistent.should.be.ok |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment