Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
brainfood
/
rivets
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Network
Create a new issue
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
7cf13491
authored
2014-01-22 12:13:50 -0800
by
Michael Richards
Browse Files
Options
Browse Files
Tag
Download
Plain Diff
Merge pull request #240 from mikeric/full-keypath-observers
Full Keypath Observers
2 parents
d82996d2
f9acdd9b
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
70 additions
and
59 deletions
Gruntfile.coffee
spec/rivets/binding.js
src/bindings.coffee
src/keypath_observer.coffee → src/observer.coffee
Gruntfile.coffee
View file @
7cf1349
...
...
@@ -20,7 +20,7 @@ module.exports = (grunt) ->
'src/view.coffee'
'src/bindings.coffee'
'src/parsers.coffee'
'src/
keypath_
observer.coffee'
'src/observer.coffee'
'src/binders.coffee'
'src/adapters.coffee'
'src/export.coffee'
...
...
spec/rivets/binding.js
View file @
7cf1349
...
...
@@ -8,7 +8,7 @@ describe('Rivets.Binding', function() {
el
=
document
.
createElement
(
'div'
);
el
.
setAttribute
(
'data-text'
,
'obj.name'
);
view
=
rivets
.
bind
(
el
,
{
obj
:
{}});
view
=
rivets
.
bind
(
el
,
{
obj
:
{
name
:
'test'
}});
binding
=
view
.
bindings
[
0
];
model
=
binding
.
model
;
});
...
...
@@ -40,12 +40,10 @@ describe('Rivets.Binding', function() {
rivets
.
config
.
preloadData
=
true
;
});
it
(
'sets the initial value
via the adapter
'
,
function
()
{
it
(
'sets the initial value'
,
function
()
{
spyOn
(
binding
,
'set'
);
spyOn
(
adapter
,
'read'
);
binding
.
bind
();
expect
(
adapter
.
read
).
toHaveBeenCalledWith
(
model
,
'name'
);
expect
(
binding
.
set
).
toHaveBeenCalled
();
expect
(
binding
.
set
).
toHaveBeenCalledWith
(
'test'
);
});
});
...
...
src/bindings.coffee
View file @
7cf1349
...
...
@@ -9,8 +9,8 @@ class Rivets.Binding
constructor
:
(
@
view
,
@
el
,
@
type
,
@
keypath
,
@
options
=
{})
->
@
formatters
=
@
options
.
formatters
||
[]
@
dependencies
=
[]
@
model
=
undefined
@
setBinder
()
@
setObserver
()
# Sets the binder to use when binding and syncing.
setBinder
:
=>
...
...
@@ -26,18 +26,6 @@ class Rivets.Binding
@
binder
or=
@
view
.
binders
[
'*'
]
@
binder
=
{
routine
:
@
binder
}
if
@
binder
instanceof
Function
# Sets a keypath observer that will notify this binding when any intermediary
# keys are changed.
setObserver
:
=>
@
observer
=
new
Rivets
.
KeypathObserver
@
view
,
@
view
.
models
,
@
keypath
,
(
obs
)
=>
@
unbind
true
if
@
key
@
model
=
obs
.
target
@
bind
true
if
@
key
@
sync
()
@
key
=
@
observer
.
key
@
model
=
@
observer
.
target
# Applies all the current formatters to the supplied value and returns the
# formatted value.
formattedValue
:
(
value
)
=>
...
...
@@ -70,10 +58,16 @@ class Rivets.Binding
# Syncs up the view binding with the model.
sync
:
=>
@
set
if
@
key
@
view
.
adapters
[
@
key
.
interface
].
read
@
model
,
@
key
.
path
else
@
model
if
@
model
isnt
@
observer
.
target
observer
.
unobserve
()
for
observer
in
@
dependencies
@
dependencies
=
[]
if
(
@
model
=
@
observer
.
target
)
?
and
@
options
.
dependencies
?
.
length
for
dependency
in
@
options
.
dependencies
observer
=
new
Rivets
.
Observer
@
view
,
@
model
,
dependency
,
@
sync
@
dependencies
.
push
observer
@
set
@
observer
.
value
()
# Publishes the value currently set on the input element back to the model.
publish
:
=>
...
...
@@ -86,41 +80,29 @@ class Rivets.Binding
if
@
view
.
formatters
[
id
]
?
.
publish
value
=
@
view
.
formatters
[
id
].
publish
value
,
args
...
@
view
.
adapters
[
@
key
.
interface
].
publish
@
model
,
@
key
.
path
,
value
@
observer
.
publish
value
# Subscribes to the model for changes at the specified keypath. Bi-directional
# routines will also listen for changes on the element to propagate them back
# to the model.
bind
:
(
silent
=
false
)
=>
@
binder
.
bind
?
.
call
@
,
@
el
unless
silent
@
view
.
adapters
[
@
key
.
interface
].
subscribe
(
@
model
,
@
key
.
path
,
@
sync
)
if
@
key
@
sync
()
if
@
view
.
config
.
preloadData
unless
silen
t
bind
:
=>
@
binder
.
bind
?
.
call
@
,
@
el
@
observer
=
new
Rivets
.
Observer
@
view
,
@
view
.
models
,
@
keypath
,
@
sync
@
model
=
@
observer
.
targe
t
if
@
options
.
dependencies
?
.
length
if
@
model
?
and
@
options
.
dependencies
?
.
length
for
dependency
in
@
options
.
dependencies
observer
=
new
Rivets
.
KeypathObserver
@
view
,
@
model
,
dependency
,
(
obs
,
prev
)
=>
key
=
obs
.
key
@
view
.
adapters
[
key
.
interface
].
unsubscribe
prev
,
key
.
path
,
@
sync
@
view
.
adapters
[
key
.
interface
].
subscribe
obs
.
target
,
key
.
path
,
@
sync
@
sync
()
key
=
observer
.
key
@
view
.
adapters
[
key
.
interface
].
subscribe
observer
.
target
,
key
.
path
,
@
sync
observer
=
new
Rivets
.
Observer
@
view
,
@
model
,
dependency
,
@
sync
@
dependencies
.
push
observer
@
sync
()
if
@
view
.
config
.
preloadData
# Unsubscribes from the model and the element.
unbind
:
(
silent
=
false
)
=>
unless
silent
unbind
:
=>
@
binder
.
unbind
?
.
call
@
,
@
el
@
observer
.
unobserve
()
@
view
.
adapters
[
@
key
.
interface
].
unsubscribe
(
@
model
,
@
key
.
path
,
@
sync
)
if
@
key
if
@
dependencies
.
length
for
obs
in
@
dependencies
key
=
obs
.
key
@
view
.
adapters
[
key
.
interface
].
unsubscribe
obs
.
target
,
key
.
path
,
@
sync
observer
.
unobserve
()
for
observer
in
@
dependencies
@
dependencies
=
[]
# Updates the binding's model from what is currently set on the view. Unbinds
...
...
@@ -190,7 +172,6 @@ class Rivets.TextBinding extends Rivets.Binding
constructor
:
(
@
view
,
@
el
,
@
type
,
@
keypath
,
@
options
=
{})
->
@
formatters
=
@
options
.
formatters
||
[]
@
dependencies
=
[]
@
setObserver
()
# A standard routine binder used for text node bindings.
binder
:
...
...
src/
keypath_
observer.coffee
→
src/observer.coffee
View file @
7cf1349
# Rivets.
Keypath
Observer
# Rivets.Observer
# ----------------------
# Parses and observes a full keypath with the appropriate adapters. Also
# intelligently re-realizes the keypath when intermediary keys change.
class
Rivets
.
Keypath
Observer
class
Rivets
.
Observer
# Performs the initial parse, variable instantiation and keypath realization.
constructor
:
(
@
view
,
@
model
,
@
keypath
,
@
callback
)
->
@
parse
()
@
objectPath
=
[]
@
target
=
@
realize
()
@
initialize
()
# Parses the keypath using the interfaces defined on the view. Sets variables
# for the tokenized keypath, as well as the end key.
...
...
@@ -25,34 +24,67 @@ class Rivets.KeypathObserver
@
tokens
=
Rivets
.
KeypathParser
.
parse
path
,
interfaces
,
root
@
key
=
@
tokens
.
pop
()
# Updates the keypath. This is called when any intermediate key is changed.
initialize
:
=>
@
objectPath
=
[]
@
target
=
@
realize
()
@
set
true
,
@
key
,
@
target
,
@
callback
if
@
target
?
# Updates the keypath. This is called when any intermediary key is changed.
update
:
=>
unless
(
next
=
@
realize
())
is
@
target
prev
=
@
target
@
set
false
,
@
key
,
@
target
,
@
callback
if
@
target
?
@
set
true
,
@
key
,
next
,
@
callback
if
next
?
oldValue
=
@
value
()
@
target
=
next
@
callback
@
,
prev
@
callback
()
unless
@
value
()
is
oldValue
adapter
:
(
key
)
=>
@
view
.
adapters
[
key
.
interface
]
set
:
(
active
,
key
,
obj
,
callback
)
=>
action
=
if
active
then
'subscribe'
else
'unsubscribe'
@
adapter
(
key
)[
action
]
obj
,
key
.
path
,
callback
read
:
(
key
,
obj
)
=>
@
adapter
(
key
).
read
obj
,
key
.
path
publish
:
(
value
)
=>
if
@
target
?
@
adapter
(
@
key
).
publish
@
target
,
@
key
.
path
,
value
value
:
=>
@
read
@
key
,
@
target
if
@
target
?
# Realizes the full keypath, attaching observers for every key and correcting
# old observers to any changed objects in the keypath.
realize
:
=>
current
=
@
model
unreached
=
null
for
token
,
index
in
@
tokens
if
current
?
if
@
objectPath
[
index
]
?
if
current
isnt
prev
=
@
objectPath
[
index
]
@
view
.
adapters
[
token
.
interface
].
unsubscribe
prev
,
token
.
path
,
@
update
@
view
.
adapters
[
token
.
interface
].
subscribe
current
,
token
.
path
,
@
update
@
set
false
,
token
,
prev
,
@
update
@
set
true
,
token
,
current
,
@
update
@
objectPath
[
index
]
=
current
else
@
view
.
adapters
[
token
.
interface
].
subscribe
current
,
token
.
path
,
@
update
@
set
true
,
token
,
current
,
@
update
@
objectPath
[
index
]
=
current
current
=
@
view
.
adapters
[
token
.
interface
].
read
current
,
token
.
path
current
=
@
read
token
,
current
else
unreached
?=
index
if
prev
=
@
objectPath
[
index
]
@
set
false
,
token
,
prev
,
@
update
@
objectPath
.
splice
unreached
if
unreached
?
current
# Unobserves any current observers set up on the keys.
unobserve
:
=>
for
token
,
index
in
@
tokens
if
obj
=
@
objectPath
[
index
]
@
view
.
adapters
[
token
.
interface
].
unsubscribe
obj
,
token
.
path
,
@
update
@
set
false
,
token
,
obj
,
@
update
...
...
Please
register
or
sign in
to post a comment