Michael
Michael

Reputation: 683

Dynamically string function calls by looping through an object in coffeescript

This is loosely related to an earlier question I posted, though with a different image editing library (CamanJS instead of Pixastic) and the code refactored somewhat.

I'm trying to write a function changify that reverts the HTML5 image canvas to its original state, and then runs a number of preset changes to it. This code, which loads the right canvas element imageClipName from a list called camanCache, works:

changify = (imageClipName) ->
    camanCache[imageClipName].revert ->
        camanCache[imageClipName]
            .brightness(10)
            .contrast(10)
            .noise(10)
            .render()
    console.log(camanCache[imageClipName])

But what I want is to find the saved preset changes from another list called imageValues and dynamically create the string of image function changes from the key value pairs of that list. Here's what I've tried to do, that fails:

imageClipName = "image_1"
imageValues = {image_1:{brightness: 10, contrast: 10, noise: 10}, image_2:{...}...}

changify = (imageClipName) ->
    camanCache[imageClipName].revert ->
        camanCache[imageClipName]
            for o in imageValues when o.id == imageClipName
                for key, val of o
                    .#{key}(val)
            .render()

This gives me a coffeescript syntax error for the third line of the changify function (Unexpected 'INDENT'), but I suspect that .#{key}(val) won't work for appending multiple functions to camanCache[imageClipName]. How to dynamically string function calls like this in coffeescript?

Upvotes: 1

Views: 291

Answers (1)

mu is too short
mu is too short

Reputation: 434625

I think you're trying to do this:

changify = (imageClipName) ->
    camanCache[imageClipName].revert -> 
        clip = camanCache[imageClipName]
        for id, methods of imageValues when id == imageClipName
            for name, arg of methods
                clip = clip[name](arg)
            clip.render()

Note the switch to for ... of in the outer loop since you're iterating over an object, for ... in is for arrays in CoffeeScript.

So given this:

{brightness: 10, contrast: 10, noise: 10}

in methods, this loop:

clip = camanCache[imageClipName]
for name, arg of methods
    clip = clip[name](arg)
clip.render()

would have the same effect as:

camanCache[imageClipName].brightness(10).contrast(10).noise(10).render()

The order of the key, val pairs when iterating over the object o is not defined so there is no guarantee about the order of the brightness, contrast, and noise calls. If you need them in a specific order then you'd have to use an array for methods:

imageValues = {image_1: [['brightness', 10], ['contrast', 10], ['noise', 10]], ... }

changify = (imageClipName) ->
    camanCache[imageClipName].revert -> 
        clip = camanCache[imageClipName]
        for id, methods of imageValues when id == imageClipName
            for method in methods
                clip = clip[method[0]](method[1])
            clip.render()

or a bit nicer:

imageValues = {image_1: [ {name: 'brightness', arg: 10}, {name: 'contrast', arg: 10}, {name: 'noise', arg: 10}], ... }

changify = (imageClipName) ->
    camanCache[imageClipName].revert -> 
        clip = camanCache[imageClipName]
        for id, methods of imageValues when id == imageClipName
            for method in methods
                clip = clip[method.name](method.arg)
            clip.render()

Upvotes: 2

Related Questions