defx-computer

This package is no longer supported and has been deprecated. To avoid malicious use, npm is hanging on to the package name.


License
ISC
Install
npm install defx-computer@1.0.0

Documentation

Computer JS

Simple computed properties.

Computer JS combines AMD-style input declaration, dependency injection, and the ability to use wildcard patterns on both input and output properties (one wildcard input per output). This allows your functions to have both active and passive dependencies, where active dependecies trigger a recomputation and passive ones just get pulled in as needed, which is great for computing accumulated values. What's more, if your computed property is accumulated (many to one, rather than one or many to many), then Computer JS will only compute that property once per cycle, regardless of how many properties matching the wildcard have actually changed value.

Per cycle? Yes. Computer JS is inherently lazy and won't do anything unless you tell it to. It has an input method to which you pass the names of any properties that have changed value. Internally, it checks to see if those properties are inputs to any of your computed props, and if so, marks those properties as dirty. Nothing actually gets computed until you call .compute(), at which point all the dirty props since the last compute get computed.

works with

  • Node
  • Browser
  • AMD

install

with npm do:

$ npm install defx-computer

with bower do:

$ bower install defx-computer

describe

var proto = {
    name: 'defx'
},
computer = new Computer(proto);

computer.def('greeting', ['name'], function (name) {
    return "hello " + name
})

computer.input('name')

/*======

compute all properties who have had one or more inputs
passed to computer.input since the last compute

======*/

computer.compute()

expect(proto.greeting).to.equal('hello defx')


proto.todos = [{
    completed: false
}, {
    completed: true
}, {
    completed: false
}]

/*======

any properties from proto can be injected after your inputs

======*/

var called = 0;

var remaining = function (completed, todos) {
    called++;
    return proto.todos.reduce(function (acc, todo) {
        return todo.completed ? acc : acc + 1
    }, 0)
}

/*======

if the i/o pattern is many to one, then the property will only be computed once
per cycle, regardless of how many properties actually changed

======*/

computer.def('remaining', ['todos.*.completed'], remaining)

computer.input('todos.0.completed')
computer.input('todos.1.completed')
computer.input('todos.2.completed')

computer.compute()

expect(proto.remaining).to.equal(2)
expect(called).to.equal(1)


it("doesnt allow inputs terminated with wildcards", function(){

    var fn = function(){
        computer.def('cities.*.population', ['cities.*'], function (city) {
        return 10000
        })
    }

    expect(fn).to.throw('input cannot be terminated with a wildcard')

})

it("doesn't allow more than one wildcard input per property", function(){

    var fn = function(){
        computer.def('totalPopulation', ['towns.*.population', 'cities.*.population'], function (_, _, towns, cities) {
            return "WTF?!"
        })
    }

    expect(fn).to.throw('only one wildcard input per property')

})

it("does allow you to pass existing computed properties as inputs to new properties", function(){

    var proto = {
        cities: [
            {
                name: 'berlin',
                population: 3502000
            },
            {
                name: 'london',
                population: 8308000
            },
            {
                name: 'copenhagen',
                population: 569557
            }
        ],
        towns: [
            {
                name: 'portree',
                population: 2491
            },
            {
                name: 'reine',
                population: 1059
            },
            {
                name: 'marsaxlokk',
                population: 3499
            }
        ]
    }

    var c = new Computer(proto)

    c
    .def('cityPopulation', ['cities.*.population'], function(_, cities){
        return cities.reduce(function(pop, city){
            return pop + city.population
        }, 0)
    })
    .def('townPopulation', ['towns.*.population'], function(_, towns){
        return towns.reduce(function(pop, town){
            return pop + town.population
        }, 0)
    })
    .def('totalPopulation', ['cityPopulation', 'townPopulation'], function(cityPop, townPop){
        return cityPop + townPop
    })

    c.input('cities.0.population')
    c.input('towns.0.population')
    c.compute()

    expect(proto.cityPopulation).to.equal(12379557)
    expect(proto.townPopulation).to.equal(7049)
    expect(proto.totalPopulation).to.equal(12386606)

})