Skip to content

Commit

Permalink
Merge pull request #655 from mikeric/fix/two-way-formatter-publish
Browse files Browse the repository at this point in the history
Fix/two way formatter publish
  • Loading branch information
benadamstyles authored Aug 9, 2016
2 parents 6e50194 + fbfe7dd commit 1c949b2
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 46 deletions.
61 changes: 57 additions & 4 deletions spec/rivets/binding.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ describe('Rivets.Binding', function() {
return value + ':' + arg1 + ':' + arg2
},
read: function(value, arg1, arg2) {
return value + '-' + arg1 + '-' + arg2
return value.replace(':' + arg1 + ':' + arg2, '')
}
}

Expand All @@ -255,8 +255,61 @@ describe('Rivets.Binding', function() {
binding.publish({target: valueInput})
adapter.set.calledWith(model, 'name', 'bobby:50:male').should.be.true

binding.set('andy')
binding.binder.routine.calledWith(valueInput, 'andy-50-male').should.be.true
valueInput.value.should.equal('bobby')

binding.set('andy:50:male')
binding.binder.routine.calledWith(valueInput, 'andy').should.be.true
})

it("should resolve formatter arguments correctly with multiple formatters", function() {
rivets.formatters.wrap = {
publish: function(value, arg1, arg2) {
return arg1 + value + arg2
},
read: function(value, arg1, arg2) {
return value.replace(arg1, '').replace(arg2, '')
}
}

rivets.formatters.saveAsCase = {
publish: function(value, typeCase) {
return value['to' + typeCase + 'Case']()
},
read: function(value, typeCase) {
return value[typeCase === 'Upper' ? 'toLowerCase' : 'toUpperCase']()
}
}

valueInput = document.createElement('input')
valueInput.setAttribute('type', 'text')
valueInput.setAttribute(
'data-value',
"obj.name | saveAsCase config.typeCase | wrap config.curly '}' | wrap config.square ']' | wrap config.paren ')'"
)

view = rivets.bind(valueInput, {
obj: {
name: 'nothing'
},
config: {
paren: '(',
square: '[',
curly: '{',
typeCase: 'Upper'
}
})

binding = view.bindings[0]
model = binding.model

valueInput.value = 'bobby'
binding.publish({target: valueInput})
adapter.set.calledWith(model, 'name', '{[(BOBBY)]}').should.be.true

valueInput.value.should.equal('bobby')

binding.set('{[(ANDY)]}')
binding.binder.routine.calledWith(valueInput, 'andy').should.be.true
})

it("should not fail or format if the specified binding function doesn't exist", function() {
Expand All @@ -277,7 +330,7 @@ describe('Rivets.Binding', function() {
binding.binder.routine.calledWith(valueInput, 'fred').should.be.true
})

it("should apply read binders left to right, and write binders right to left", function() {
it("should apply read binders left to right, and publish binders right to left", function() {
rivets.formatters.totally = {
publish: function(value) { return value + ' totally' },
read: function(value) { return value + ' totally' }
Expand Down
45 changes: 27 additions & 18 deletions src/bindings.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,30 @@ class Rivets.Binding
parseTarget: =>
token = Rivets.TypeParser.parse @keypath

if token.type is 0
if token.type is Rivets.TypeParser.types.primitive
@value = token.value
else
@observer = @observe @view.models, @keypath, @sync
@model = @observer.target

parseFormatterArguments: (args, formatterIndex) =>
args = (Rivets.TypeParser.parse(arg) for arg in args)
processedArgs = []

for arg, ai in args
processedArgs.push if arg.type is Rivets.TypeParser.types.primitive
arg.value
else
@formatterObservers[formatterIndex] or= {}

unless observer = @formatterObservers[formatterIndex][ai]
observer = @observe @view.models, arg.value, @sync
@formatterObservers[formatterIndex][ai] = observer

observer.value()

processedArgs

# Applies all the current formatters to the supplied value and returns the
# formatted value.
formattedValue: (value) =>
Expand All @@ -49,20 +67,7 @@ class Rivets.Binding
id = args.shift()
formatter = @view.formatters[id]

args = (Rivets.TypeParser.parse(arg) for arg in args)
processedArgs = []

for arg, ai in args
processedArgs.push if arg.type is 0
arg.value
else
@formatterObservers[fi] or= {}

unless observer = @formatterObservers[fi][ai]
observer = @observe @view.models, arg.value, @sync
@formatterObservers[fi][ai] = observer

observer.value()
processedArgs = @parseFormatterArguments args, fi

if formatter?.read instanceof Function
value = formatter.read.call @model, value, processedArgs...
Expand Down Expand Up @@ -107,13 +112,17 @@ class Rivets.Binding
publish: =>
if @observer
value = @getValue @el
lastformatterIndex = @formatters.length - 1

for formatter in @formatters.slice(0).reverse()
for formatter, fiReversed in @formatters.slice(0).reverse()
fi = lastformatterIndex - fiReversed
args = formatter.split /\s+/
id = args.shift()

processedArgs = @parseFormatterArguments args, fi

if @view.formatters[id]?.publish
value = @view.formatters[id].publish value, args...
value = @view.formatters[id].publish value, processedArgs...

@observer.setValue value

Expand Down Expand Up @@ -181,7 +190,7 @@ class Rivets.ComponentBinding extends Rivets.Binding

if propertyName in (@component.static ? [])
@static[propertyName] = attribute.value
else if token.type is 0
else if token.type is Rivets.TypeParser.types.primitive
@static[propertyName] = token.value
else
@observers[propertyName] = attribute.value
Expand Down
48 changes: 24 additions & 24 deletions src/parsers.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,30 @@ class Rivets.TypeParser
keypath: 1

@parse: (string) ->
if /^'.*'$|^".*"$/.test string
type: @types.primitive
value: string.slice 1, -1
else if string is 'true'
type: @types.primitive
value: true
else if string is 'false'
type: @types.primitive
value: false
else if string is 'null'
type: @types.primitive
value: null
else if string is 'undefined'
type: @types.primitive
value: undefined
else if string is ''
type: @types.primitive
value: undefined
else if isNaN(Number(string)) is false
type: @types.primitive
value: Number string
else
type: @types.keypath
value: string
if /^'.*'$|^".*"$/.test string
type: @types.primitive
value: string.slice 1, -1
else if string is 'true'
type: @types.primitive
value: true
else if string is 'false'
type: @types.primitive
value: false
else if string is 'null'
type: @types.primitive
value: null
else if string is 'undefined'
type: @types.primitive
value: undefined
else if string is ''
type: @types.primitive
value: undefined
else if isNaN(Number(string)) is false
type: @types.primitive
value: Number string
else
type: @types.keypath
value: string

# Rivets.TextTemplateParser
# -------------------------
Expand Down

0 comments on commit 1c949b2

Please sign in to comment.