Various updates.
This commit is contained in:
parent
084ba95b6a
commit
08a84df7d8
@ -6,6 +6,8 @@ $_ = require 'underscore'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
# TODO: move these helpers in a dedicated module.
|
||||
|
||||
$done = {}
|
||||
|
||||
# Similar to `$_.each()` but can be interrupted by returning the
|
||||
@ -28,7 +30,34 @@ $each = (col, iterator, ctx) ->
|
||||
|
||||
$makeFunction = (val) -> -> val
|
||||
|
||||
# Similar to `$_.map()` but change the current collection.
|
||||
# Similar to `$_.map()` for array and `$_.mapValues()` for objects.
|
||||
#
|
||||
# Note: can be interrupted by returning the special value `done`
|
||||
# provided as the forth argument.
|
||||
$map = (col, iterator, ctx) ->
|
||||
# The default context is inherited.
|
||||
ctx ?= this
|
||||
|
||||
if (n = col.length)?
|
||||
result = []
|
||||
# Array-like object.
|
||||
i = 0
|
||||
while i < n
|
||||
value = iterator.call ctx, col[i], "#{i}", col, $done
|
||||
break if value is $done
|
||||
result.push value
|
||||
++i
|
||||
else
|
||||
result = {}
|
||||
for key of col
|
||||
value = iterator.call ctx, col[key], key, $done
|
||||
break if value is $done
|
||||
result.push value
|
||||
|
||||
# The new collection is returned.
|
||||
result
|
||||
|
||||
# Similar to `$map()` but change the current collection.
|
||||
#
|
||||
# Note: can be interrupted by returning the special value `done`
|
||||
# provided as the forth argument.
|
||||
@ -223,27 +252,20 @@ class $MappedCollection2 extends $EventEmitter
|
||||
#--------------------------------
|
||||
|
||||
get: (keys) ->
|
||||
singular = $_.isString keys
|
||||
items = $mapInPlace (@_fetchItems keys), (item) -> item.val
|
||||
if singular
|
||||
items[0]
|
||||
if keys is undefined
|
||||
items = $_.map @_byKey, (item) -> item.val
|
||||
else
|
||||
items
|
||||
items = $mapInPlace (@_fetchItems keys), (item) -> item.val
|
||||
|
||||
if $_.isString keys then items[0] else items
|
||||
|
||||
getRaw: (keys) ->
|
||||
singular = $_.isString keys
|
||||
items = @_fetchItems keys
|
||||
if singular
|
||||
items[0]
|
||||
if keys is undefined
|
||||
items = item for _, item of @_byKey
|
||||
else
|
||||
items
|
||||
items = @_fetchItems keys
|
||||
|
||||
getAll: ->
|
||||
items = {}
|
||||
|
||||
items[key] = val for key, {val} of @_byKey
|
||||
|
||||
items
|
||||
if $_.isString keys then items[0] else items
|
||||
|
||||
remove: (keys) ->
|
||||
@_removeItems (@_fetchItems keys)
|
||||
@ -339,7 +361,7 @@ class $MappedCollection2 extends $EventEmitter
|
||||
|
||||
# Forces items to update their value.
|
||||
touch: (keys) ->
|
||||
@_updateItems (@_fetchItems keys)
|
||||
@_updateItems (@_fetchItems keys, true)
|
||||
|
||||
#--------------------------------
|
||||
|
||||
@ -366,17 +388,21 @@ class $MappedCollection2 extends $EventEmitter
|
||||
# One for everything.
|
||||
@emit "any", event, items
|
||||
|
||||
_fetchItems: (keys) ->
|
||||
_fetchItems: (keys, ignoreMissingItems = false) ->
|
||||
unless $_.isArray keys
|
||||
keys = if $_.isObject keys then $_.keys keys else [keys]
|
||||
|
||||
items = []
|
||||
for key in keys
|
||||
item = @_byKey[key]
|
||||
@_assert(
|
||||
item?
|
||||
"no item with key “#{key}”"
|
||||
)
|
||||
item
|
||||
if item?
|
||||
items.push item
|
||||
else
|
||||
@_assert(
|
||||
ignoreMissingItems
|
||||
"no item with key “#{key}”"
|
||||
)
|
||||
items
|
||||
|
||||
_removeItems: (items) ->
|
||||
return if $_.isEmpty items
|
||||
|
@ -2,6 +2,9 @@ $_ = require 'underscore'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
$asArray = (val) -> if $_.isArray val then val else [val]
|
||||
$asFunction = (val) -> if $_.isFunction val then val else -> val
|
||||
|
||||
$removeValue = (array, value) ->
|
||||
index = array.indexOf value
|
||||
return false if index is -1
|
||||
@ -10,24 +13,64 @@ $removeValue = (array, value) ->
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
# TODO: currently the watch can be updated multiple times per
|
||||
# “$MappedCollection2.set()” which is inefficient: there should be
|
||||
# possible to address that.
|
||||
|
||||
$watch = (collection, {
|
||||
key
|
||||
# Key(s) of the “remote” objects watched.
|
||||
#
|
||||
# If it is a function, it is evaluated in the scope of the “current”
|
||||
# object. (TODO)
|
||||
#
|
||||
# Default: undefined
|
||||
keys
|
||||
|
||||
rule
|
||||
# Alias for `keys`.
|
||||
key
|
||||
|
||||
# Rule(s) of the “remote” objects watched.
|
||||
#
|
||||
# If it is a function, it is evaluated in the scope of the “current”
|
||||
# object. (TODO)
|
||||
#
|
||||
# Note: `key`/`keys` and `rule`/`rules` cannot be used both.
|
||||
#
|
||||
# Default: undefined
|
||||
rules
|
||||
|
||||
# Alias for `rules`.
|
||||
rule
|
||||
|
||||
# Value to add to the set.
|
||||
#
|
||||
# If it is a function, it is evaluated in the scope of the “remote”
|
||||
# object.
|
||||
#
|
||||
# Default: -> @val
|
||||
val
|
||||
|
||||
# Predicates the “remote” object must fulfill to be used.
|
||||
#
|
||||
# Default: -> true
|
||||
if: cond
|
||||
|
||||
# Function evaluated in the scope of the “remote” object which
|
||||
# returns the key of the object to update (usually the current one).
|
||||
#
|
||||
# TODO: Does it make sense to return an array?
|
||||
#
|
||||
# Default: undefined
|
||||
bind
|
||||
|
||||
# Initial value.
|
||||
init
|
||||
}, fn) ->
|
||||
# The default value is simply the value of the item.
|
||||
val ?= -> @val
|
||||
val = if val is undefined
|
||||
# The default value is simply the value of the item.
|
||||
-> @val
|
||||
else
|
||||
$asFunction val
|
||||
|
||||
watcher = {
|
||||
# Method allowing the cleanup when the helper is no longer used.
|
||||
@ -44,8 +87,9 @@ $watch = (collection, {
|
||||
|
||||
# Returns the value for this item if any or the common value.
|
||||
values = watcher.values
|
||||
if key of values
|
||||
values["$#{key}"]
|
||||
namespace = "$#{key}"
|
||||
if namespace of values
|
||||
values[namespace]
|
||||
else
|
||||
values.common
|
||||
|
||||
@ -61,13 +105,18 @@ $watch = (collection, {
|
||||
$_.each items, (item) ->
|
||||
return unless not cond? or cond.call item
|
||||
|
||||
# Compute the current value.
|
||||
value = val.call item
|
||||
if bind?
|
||||
key = bind.call item
|
||||
|
||||
namespace = if bind?
|
||||
"$#{bind.call item}"
|
||||
# If bind did return a key, ignores this value.
|
||||
return unless key?
|
||||
|
||||
namespace = "$#{key}"
|
||||
else
|
||||
'common'
|
||||
namespace = 'common'
|
||||
|
||||
# Computes the current value.
|
||||
value = val.call item
|
||||
|
||||
(valuesByNamespace[namespace] ?= []).push value
|
||||
|
||||
@ -99,24 +148,36 @@ $watch = (collection, {
|
||||
# Sets up the watch based on the provided criteria.
|
||||
#
|
||||
# TODO: provides a way to clean this when no longer used.
|
||||
keys ?= []
|
||||
rules ?= []
|
||||
keys.push key if key?
|
||||
rules.push rule if rule?
|
||||
keys = $asArray (keys ? key ? [])
|
||||
rules = $asArray (rules ? rule ? [])
|
||||
if not $_.isEmpty keys
|
||||
# Matching is done on the keys.
|
||||
|
||||
throw new Error 'cannot use keys and rules' unless $_.isEmpty rules
|
||||
|
||||
$_.each keys, (key) -> collection.on "key=#{key}", processOne
|
||||
else if not $_isEmpty rules
|
||||
|
||||
# Handles existing items.
|
||||
process 'enter', collection.getRaw keys
|
||||
else if not $_.isEmpty rules
|
||||
# Matching is done the rules.
|
||||
|
||||
$_.each rules, (rule) -> collection.on "rule=#{rule}", process
|
||||
|
||||
# TODO: Inefficient, is there another way?
|
||||
rules = do -> # Minor optimization.
|
||||
tmp = Object.create null
|
||||
tmp[rule] = true for rule in rules
|
||||
tmp
|
||||
$_.each collection.getRaw(), (item) ->
|
||||
processOne 'enter', item if item.rule of rules
|
||||
else
|
||||
# No matching done.
|
||||
|
||||
collection.on 'any', updateMultiple
|
||||
collection.on 'any', process
|
||||
|
||||
# Handles existing items.
|
||||
process 'enter', collection.getRaw()
|
||||
|
||||
# Returns the watcher object.
|
||||
watcher
|
||||
|
@ -37,189 +37,190 @@ describe 'Helper', ->
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
describe '$set', ->
|
||||
|
||||
describe 'with key', ->
|
||||
|
||||
beforeEach ->
|
||||
collection.rule watcher: ->
|
||||
@val = $set {
|
||||
keys: [
|
||||
'foo'
|
||||
'bar'
|
||||
]
|
||||
val: -> @val
|
||||
}
|
||||
|
||||
it 'should works with new items', ->
|
||||
# Register a watcher.
|
||||
collection.set {
|
||||
watcher: null
|
||||
}
|
||||
|
||||
# The set must be empty.
|
||||
set = collection.get 'watcher'
|
||||
$expect(set).to.have.members []
|
||||
|
||||
# Register a watched item.
|
||||
collection.set {
|
||||
foo: 'foo 1'
|
||||
}
|
||||
|
||||
# The set must contains the value of foo.
|
||||
set = collection.get 'watcher'
|
||||
$expect(set).to.have.members [
|
||||
collection.get 'foo'
|
||||
]
|
||||
|
||||
# Register another watched item.
|
||||
collection.set {
|
||||
bar: 'bar'
|
||||
}
|
||||
|
||||
# The set must contains the values of foo and bar.
|
||||
set = collection.get 'watcher'
|
||||
$expect(set).to.have.members [
|
||||
collection.get 'foo'
|
||||
collection.get 'bar'
|
||||
]
|
||||
|
||||
# Updates foo and deletes bar.
|
||||
collection.set {
|
||||
foo: 'foo 2'
|
||||
}
|
||||
collection.remove 'bar'
|
||||
|
||||
# The set must contains the value of foo.
|
||||
set = collection.get 'watcher'
|
||||
$expect(set).to.have.members [
|
||||
collection.get 'foo'
|
||||
]
|
||||
|
||||
# Deletes foo.
|
||||
collection.remove 'foo'
|
||||
|
||||
# The set must be empty.
|
||||
set = collection.get 'watcher'
|
||||
$expect(set).to.have.members []
|
||||
|
||||
it 'should works with previous items', ->
|
||||
# Register watched items.
|
||||
collection.set {
|
||||
foo: 'foo'
|
||||
bar: 'bar'
|
||||
}
|
||||
|
||||
# Register a watcher.
|
||||
collection.set {
|
||||
watcher: null
|
||||
}
|
||||
|
||||
# The set must be an array containing only the value of foo
|
||||
# and bar.
|
||||
set = collection.get 'watcher'
|
||||
$expect(set).to.have.members [
|
||||
collection.get 'foo'
|
||||
collection.get 'bar'
|
||||
]
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
# All helpers share the same logical code, we need only to test one
|
||||
# extensively and test the others basically.
|
||||
#
|
||||
# $sum was chosen because it is the simplest helper to test.
|
||||
describe '$sum', ->
|
||||
|
||||
describe 'with key', ->
|
||||
it 'with single key', ->
|
||||
collection.set foo: 1
|
||||
|
||||
beforeEach ->
|
||||
collection.rule watcher: ->
|
||||
@val = $sum {
|
||||
keys: [
|
||||
'foo'
|
||||
'bar'
|
||||
]
|
||||
}
|
||||
|
||||
it 'should works with new items', ->
|
||||
# Register a watcher.
|
||||
collection.set {
|
||||
watcher: null
|
||||
collection.item sum: ->
|
||||
@val = $sum {
|
||||
key: 'foo'
|
||||
}
|
||||
|
||||
# The sum must null.
|
||||
sum = collection.get 'watcher'
|
||||
$expect(sum).to.equal 0
|
||||
$expect(collection.get 'sum').to.equal 1
|
||||
|
||||
# Register a watched item.
|
||||
collection.set {
|
||||
foo: 1
|
||||
collection.set foo:2
|
||||
|
||||
$expect(collection.get 'sum').to.equal 2
|
||||
|
||||
collection.remove 'foo'
|
||||
|
||||
$expect(collection.get 'sum').to.equal 0
|
||||
|
||||
it 'with multiple keys', ->
|
||||
collection.set {
|
||||
foo: 1
|
||||
bar: 2
|
||||
}
|
||||
|
||||
collection.item sum: ->
|
||||
@val = $sum {
|
||||
keys: ['foo', 'bar']
|
||||
}
|
||||
|
||||
# The sum must contains the value of foo.
|
||||
sum = collection.get 'watcher'
|
||||
$expect(sum).to.equal (collection.get 'foo')
|
||||
$expect(collection.get 'sum').to.equal 3
|
||||
|
||||
# Register another watched item.
|
||||
collection.set {
|
||||
bar: 2
|
||||
collection.set bar:3
|
||||
|
||||
$expect(collection.get 'sum').to.equal 4
|
||||
|
||||
collection.remove 'foo'
|
||||
|
||||
$expect(collection.get 'sum').to.equal 3
|
||||
|
||||
it 'with dynamic keys', ->
|
||||
collection.set {
|
||||
foo: 1
|
||||
bar: 2
|
||||
}
|
||||
|
||||
collection.rule sum: ->
|
||||
@val = $sum {
|
||||
key: -> (@key.split '.')[1]
|
||||
}
|
||||
collection.set {
|
||||
'sum.foo': null
|
||||
'sum.bar': null
|
||||
}
|
||||
|
||||
$expect(collecter.get 'sum.foo').to.equal 1
|
||||
$expect(collecter.get 'sum.bar').to.equal 2
|
||||
|
||||
collection.remove 'bar'
|
||||
|
||||
$expect(collecter.get 'sum.foo').to.equal 1
|
||||
$expect(collecter.get 'sum.bar').to.equal 0
|
||||
|
||||
it 'with single rule', ->
|
||||
collection.set {
|
||||
'foo.1': 1
|
||||
'foo.2': 2
|
||||
}
|
||||
|
||||
collection.item sum: ->
|
||||
@val = $sum {
|
||||
rule: 'foo'
|
||||
}
|
||||
|
||||
# The sum must contains the values of foo and bar.
|
||||
sum = collection.get 'watcher'
|
||||
$expect(sum).to.equal ((collection.get 'foo') + (collection.get 'bar'))
|
||||
$expect(collection.get 'sum').to.equal 3
|
||||
|
||||
# Updates foo and deletes bar.
|
||||
collection.set {
|
||||
foo: 3
|
||||
}
|
||||
collection.remove 'bar'
|
||||
collection.set 'foo.2':3
|
||||
|
||||
# The sum must contains the value of foo.
|
||||
sum = collection.get 'watcher'
|
||||
$expect(sum).to.equal (collection.get 'foo')
|
||||
$expect(collection.get 'sum').to.equal 4
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
collection.remove 'foo.1'
|
||||
|
||||
describe '$val', ->
|
||||
$expect(collection.get 'sum').to.equal 3
|
||||
|
||||
describe 'with key', ->
|
||||
it 'with multiple rules', ->
|
||||
collection.set {
|
||||
'foo': 1
|
||||
'bar.1': 2
|
||||
'bar.2': 3
|
||||
}
|
||||
|
||||
def = 'no value'
|
||||
|
||||
beforeEach ->
|
||||
collection.rule watcher: ->
|
||||
@val = $val {
|
||||
keys: [
|
||||
'foo'
|
||||
]
|
||||
default: def
|
||||
}
|
||||
|
||||
it 'should works with new items', ->
|
||||
# Register a watcher.
|
||||
collection.set {
|
||||
watcher: null
|
||||
collection.item sum: ->
|
||||
@val = $sum {
|
||||
rules: ['foo', 'bar']
|
||||
}
|
||||
|
||||
# The value must equals the default value.
|
||||
value = collection.get 'watcher'
|
||||
$expect(value).to.equal def
|
||||
$expect(collection.get 'sum').to.equal 6
|
||||
|
||||
# Register a watched item.
|
||||
collection.set {
|
||||
foo: 'foo'
|
||||
collection.set 'bar.1':3
|
||||
|
||||
$expect(collection.get 'sum').to.equal 7
|
||||
|
||||
collection.remove 'bar.2'
|
||||
|
||||
$expect(collection.get 'sum').to.equal 4
|
||||
|
||||
it 'with bind', ->
|
||||
collection.set {
|
||||
'foo': {
|
||||
sum: 2 # This item will participate to `sum.2`.
|
||||
val: 1
|
||||
}
|
||||
'bar': {
|
||||
sum: 1 # This item will participate to `sum.1`.
|
||||
val: 2
|
||||
}
|
||||
}
|
||||
|
||||
collection.rule sum: ->
|
||||
@val = $sum {
|
||||
bind: ->
|
||||
id = @val.sum
|
||||
return unless id?
|
||||
"sum.#{id}"
|
||||
val: -> @val.val
|
||||
}
|
||||
collection.set {
|
||||
'sum.1': null
|
||||
'sum.2': null
|
||||
}
|
||||
|
||||
$expect(collection.get 'sum.1').equal 2
|
||||
$expect(collection.get 'sum.2').equal 1
|
||||
|
||||
it 'with predicate', ->
|
||||
collection.set {
|
||||
foo: 1
|
||||
bar: 2
|
||||
baz: 3
|
||||
}
|
||||
|
||||
collection.item sum: ->
|
||||
@val = $sum {
|
||||
if: -> /^b/.test @rule
|
||||
}
|
||||
|
||||
# The value must contains the value of foo.
|
||||
value = collection.get 'watcher'
|
||||
$expect(value).to.equal (collection.get 'foo')
|
||||
$expect(collection.get 'sum').equal 5
|
||||
|
||||
# Deletes foo.
|
||||
collection.remove 'foo'
|
||||
collection.set foo:4
|
||||
|
||||
# The value must equals the default value.
|
||||
value = collection.get 'watcher'
|
||||
$expect(value).to.equal def
|
||||
$expect(collection.get 'sum').equal 5
|
||||
|
||||
collection.set bar:5
|
||||
|
||||
$expect(collection.get 'sum').equal 8
|
||||
|
||||
collection.remove 'baz'
|
||||
|
||||
$expect(collection.get 'sum').equal 5
|
||||
|
||||
it 'with initial value', ->
|
||||
collection.set foo: 1
|
||||
|
||||
collection.item sum: ->
|
||||
@val = $sum {
|
||||
key: 'foo'
|
||||
init: 2
|
||||
}
|
||||
|
||||
$expect(collection.get 'sum').to.equal 3
|
||||
|
||||
collection.set foo:2
|
||||
|
||||
$expect(collection.get 'sum').to.equal 4
|
||||
|
||||
collection.remove 'foo'
|
||||
|
||||
$expect(collection.get 'sum').to.equal 2
|
||||
|
||||
# TODO:
|
||||
# - bind
|
||||
# - dynamic key
|
||||
# - handle previously existing items.
|
||||
# - dynamic keys
|
||||
# - dynamic rules
|
||||
|
@ -1,54 +1,3 @@
|
||||
# FIXME: function executed each time vs dynamic fields.
|
||||
|
||||
$set = ({
|
||||
# Identifier of the “remote” object watched.
|
||||
#
|
||||
# If it is a function, it is evaluated in the scope of the “current”
|
||||
# object.
|
||||
#
|
||||
# TODO: Can also be an array.
|
||||
#
|
||||
# Default: undefined
|
||||
key
|
||||
|
||||
# Rule to watch (everything if null/undefined).
|
||||
#
|
||||
# Default: undefined
|
||||
rule
|
||||
|
||||
# Value to add to the set.
|
||||
#
|
||||
# If it is a function, it is evaluated in the scope of the “remote”
|
||||
# object.
|
||||
#
|
||||
# Default: -> @key
|
||||
val
|
||||
|
||||
# Predicates which must be fulfilled for the “remote” object to be
|
||||
# used.
|
||||
#
|
||||
# Default: true
|
||||
if: cond
|
||||
|
||||
# Identifier of the object to update (usually the current one).
|
||||
#
|
||||
# Usually this entry is a function which is evaluated in the scope
|
||||
# of the “remote” object.
|
||||
#
|
||||
# TODO: Can also be an array.
|
||||
#
|
||||
# Default: undefined
|
||||
bind
|
||||
}) ->
|
||||
val ?= -> @key
|
||||
# TODO
|
||||
|
||||
$sum = ({rule, val, if: cond, bind}) ->
|
||||
# TODO
|
||||
|
||||
$map = ({key, rule, val, if: cond, bind}) ->
|
||||
# TODO
|
||||
|
||||
isVMRunning = ->
|
||||
switch @power_state
|
||||
when 'Paused', 'Running'
|
||||
|
Loading…
Reference in New Issue
Block a user