9db97f68 by Michael Richards

Make the keypath observer handle the full spectrum of the keypath and unresolvab…

…le targets without error. [#233]
1 parent 8e267cc5
1 # Rivets.KeypathObserver 1 # Rivets.Observer
2 # ---------------------- 2 # ----------------------
3 3
4 # Parses and observes a full keypath with the appropriate adapters. Also 4 # Parses and observes a full keypath with the appropriate adapters. Also
5 # intelligently re-realizes the keypath when intermediary keys change. 5 # intelligently re-realizes the keypath when intermediary keys change.
6 class Rivets.KeypathObserver 6 class Rivets.Observer
7 # Performs the initial parse, variable instantiation and keypath realization. 7 # Performs the initial parse, variable instantiation and keypath realization.
8 constructor: (@view, @model, @keypath, @callback) -> 8 constructor: (@view, @model, @keypath, @callback) ->
9 @parse() 9 @parse()
10 @objectPath = [] 10 @initialize()
11 @target = @realize()
12 11
13 # Parses the keypath using the interfaces defined on the view. Sets variables 12 # Parses the keypath using the interfaces defined on the view. Sets variables
14 # for the tokenized keypath, as well as the end key. 13 # for the tokenized keypath, as well as the end key.
...@@ -25,34 +24,63 @@ class Rivets.KeypathObserver ...@@ -25,34 +24,63 @@ class Rivets.KeypathObserver
25 @tokens = Rivets.KeypathParser.parse path, interfaces, root 24 @tokens = Rivets.KeypathParser.parse path, interfaces, root
26 @key = @tokens.pop() 25 @key = @tokens.pop()
27 26
28 # Updates the keypath. This is called when any intermediate key is changed. 27 initialize: =>
28 @objectPath = []
29 @target = @realize()
30 @set true, @key, @target, @callback if @target?
31
32 # Updates the keypath. This is called when any intermediary key is changed.
29 update: => 33 update: =>
30 unless (next = @realize()) is @target 34 unless (next = @realize()) is @target
31 prev = @target 35 @set false, @key, @target, @callback if @target?
36 @set true, @key, next, @callback if next?
37
38 oldValue = @value()
32 @target = next 39 @target = next
33 @callback @, prev 40 @callback() unless @value() is oldValue
41
42 adapter: (key) =>
43 @view.adapters[key.interface]
44
45 set: (active, key, obj, callback) =>
46 action = if active then 'subscribe' else 'unsubscribe'
47 @adapter(key)[action] obj, key.path, callback
48
49 read: (key, obj) =>
50 @adapter(key).read obj, key.path
51
52 value: =>
53 @read @key, @target if @target?
34 54
35 # Realizes the full keypath, attaching observers for every key and correcting 55 # Realizes the full keypath, attaching observers for every key and correcting
36 # old observers to any changed objects in the keypath. 56 # old observers to any changed objects in the keypath.
37 realize: => 57 realize: =>
38 current = @model 58 current = @model
59 unreached = null
39 60
40 for token, index in @tokens 61 for token, index in @tokens
41 if @objectPath[index]? 62 if current?
42 if current isnt prev = @objectPath[index] 63 if @objectPath[index]?
43 @view.adapters[token.interface].unsubscribe prev, token.path, @update 64 if current isnt prev = @objectPath[index]
44 @view.adapters[token.interface].subscribe current, token.path, @update 65 @set false, token, prev, @update
66 @set true, token, current, @update
67 @objectPath[index] = current
68 else
69 @set true, token, current, @update
45 @objectPath[index] = current 70 @objectPath[index] = current
71
72 current = @read token, current
46 else 73 else
47 @view.adapters[token.interface].subscribe current, token.path, @update 74 unreached ?= index
48 @objectPath[index] = current
49 75
50 current = @view.adapters[token.interface].read current, token.path 76 if prev = @objectPath[index]
77 @set false, token, prev, @update
51 78
79 @objectPath.splice unreached if unreached?
52 current 80 current
53 81
54 # Unobserves any current observers set up on the keys. 82 # Unobserves any current observers set up on the keys.
55 unobserve: => 83 unobserve: =>
56 for token, index in @tokens 84 for token, index in @tokens
57 if obj = @objectPath[index] 85 if obj = @objectPath[index]
58 @view.adapters[token.interface].unsubscribe obj, token.path, @update 86 @set false, token, obj, @update
......