core-state
Version:
163 lines (157 loc) • 5.23 kB
text/coffeescript
Errors = require "./errors"
EventEmitter = (require "eventex").EventEmitter
class CoreState extends EventEmitter
constructor:()->
super()
@groups = {
}
register:(maintainer,option = {})->
if @groups[maintainer.group]?[maintainer.name] and option.safe
return false
@groups[maintainer.group] ?= {}
@groups[maintainer.group][maintainer.name] = maintainer
@emit "init",maintainer.getProperty()
unregister:(maintainer)->
if maintainer instanceof PropertyMaintainer
group = @groups[maintainer.group] or {}
delete group[maintainer.name]
maintainer.stopListenBy this
@emit "delete",maintainer.path
return true
else
path = maintainer
[group,name] = path.split(".")
group = @groups[group]
if not group
return false
maintainer = group[name]
if not maintainer
return false
delete group[name]
maintainer.stopListenBy this
@emit "delete",maintainer.path
return true
get:(path)->
routes = path.split(".")
groupName = routes.shift()
propertyName = routes.shift()
if not groupName
return
group = @groups[groupName] or {}
if not propertyName or propertyName is "*"
return (group[prop].getProperty() for prop of group)
property = group[propertyName]
if not property
return null
return property.getProperty()
createMaintainer:(name,group,data)->
pm = new PropertyMaintainer(this,name,group)
pm.data = data
@register(pm)
return pm
all:()->
result = {}
for prop of @groups
result[prop] = {}
group = @groups[prop]
for name of group
result[prop][name] = group[name].data
return result
print:(option = {})->
result = @all()
log = option.log or console.log.bind console
log result
class PropertyMaintainer extends EventEmitter
@defaultGroup = "default"
constructor:(@state,@name,@group = PropertyMaintainer.defaultGroup)->
super()
@path = "#{@group}.#{@name}"
@version = 1000
@data = null
getProperty:()->
return {
@data,@version,@path
}
getValueByPath:(routes = [])->
# currently not used because I haven't thought of a perfect
# subfield watch scheme and graceful usecase.
data = @data
while (prop = routes.shift()) and data
data = data[prop]
if routes.length is 0
return data
return
setValue:(data)->
@version += 1
@data = data
@state.emit "change",this.getProperty()
updateValue:(partial)->
# only update part of the value
throw new Errors.NotImplemented("I will do this latter")
unregister:()->
@state?.unregister this
class ObserveWindow extends EventEmitter
constructor:(@coreState)->
super()
@watches = []
@coreState.listenBy this,"delete",(path)=>
if @match path
@emit "delete",path
@coreState.listenBy this,"init",(property)=>
if @match property.path
@emit "init",property
@coreState.listenBy this,"change",(property)=>
if @match property.path
@emit "change",property
destroy:()->
@coreState.stopListenBy this
match:(target)->
for path in @watches
if target.slice(0,path.length) is path
return true
return false
observe:(path = "")->
path = @normalize(path)
if path not in @watches
@watches.push path
return @coreState.get(path)
normalize:(path)->
path = path.split(".")
group = path.shift()
if not group
return
name = path.shift()
if not name or name is "*"
name = ""
path = "#{group}.#{name}"
return path
stopObserve:(path = "")->
path = @normalize(path)
@watches = @watches.filter (watch)->
return watch isnt path
stopObserves:(pathes)->
pathes = pathes.map (path)=>@normalize path
@watches = @watches.filter (watch)->
return watch not in pathes
observes:(pathes)->
results = []
for path in pathes
result = @observe(path)
if not resutl
continue
if result instanceof Array
results.push result...
else
results.push result
results.sort (a,b)->
if a.path > b.path
return 1
else if a.path < b.path
return -1
return 0
results = results.filter (item,index)->
# only use those not equals next
return item.path isnt results[index+1]?.path
module.exports.CoreState = CoreState
module.exports.ObserveWindow = ObserveWindow
module.exports.PropertyMaintainer = PropertyMaintainer