New feature: computed properties

I couldn't resist sneaking one more feature in before the upcoming 0.4.0 release, since it's something I need in my current project. In the spirit of the collaborative git workflow we're hammering out on the [new mailing list](https://groups.google.com/forum/#!topic/ractive-js/7HFPdWB-aRk) I'm submitting it as a PR to give everyone the opportunity to offer any feedback.

The idea is fairly simple: you can define computed properties that update reactively based on their dependencies. Previously, you might have done something like this:

```js
ractive = new Ractive({
  el: 'body',
  template: '{{width}} * {{height}} = {{ area() }}', // note the function invocation
  data: {
    width: 100,
    height: 100,
    area: function () { return this.get( 'width' ) * this.get( 'height' ); }
  }
});
```

That's nice and all - the `{{ area() }}` mustache updates reactively as `width` and `height` change - but it's limited. To get the area value programmatically you'd have to do something ridiculous like...

```js
area = ractive.get('area').call(ractive);
```

...which effectively prevents you from composing computed values together in any meaningful way. And you can't 'observe' the area outside of the template, without doing something like this:

```js
ractive.observe( 'width height', function () {
	var area = this.get( 'width' ) + this.get( 'height' );
	doSomething( area );
});
```


## Computed properties to the rescue

Now, you can do

```js
ractive = new Ractive({
  el: 'body',
  template: '{{width}} * {{height}} = {{area}}', // `area` looks like a regular property
  data: {
    width: 100,
    height: 100
  },
  computed: {
    area: function () { return this.get( 'width' ) * this.get( 'height' ); }
  }
});
```

With this, the `area` property can be treated like any other. It will update reactively (because the calls to `ractive.get()` tell Ractive that it should be recomputed when `width` or `height` change), so you can do...

```js
ractive.observe( 'area', doSomething );
```

...instead of manually recalculating it. And computed values can depend on other computed values, and so on (before anyone asks, we're not doing a topological sort or anything fancy like that - in real world scenarios I'd expect the overhead of doing the sort to be greater than the cost of occasionally recomputing a node in the dependency graph more times than is required).



## Compact syntax

The syntax used above, where each computed property is defined as a function, gives you a lot of flexibility. But there's a more compact string syntax you can use:

```js
ractive = new Ractive({
  ...,
  computed: {
    area: '${width} * ${height}'
  }
});
```

This string is turned into a function with the `Function` constructor (which unfortunately means it isn't [CSP compliant](https://developer.mozilla.org/en-US/docs/Security/CSP)) - any `${...}` blocks are basically turned into `ractive.get('...')`, so it works exactly the same way. Needless to say you can use any JavaScript here - `${foo}.toUpperCase()`, `Math.round(${num})`, and so on.



## Setting computed values

By default, computed values are read-only, and if you try to `ractive.set('someComputedProperty')` an error will be thrown. But you can use a third syntax option which allows you to declare a `set()` method:

```js
ractive = new Ractive({
  data: { firstname: 'Douglas', lastname: 'Crockford' },
  computed: {
    fullname: {
      get: '${firstname} + " " + ${lastname}', // or use the function syntax
      set: function ( fullname ) {
        var names = fullname.split( ' ' );

        this.set({
          firstname: names[0] || '',
          lastname: names[1] || ''
        });
      }
    }
  }
});

ractive.set( 'fullname', 'Brendan Eich' );

ractive.get( 'firstname' ); // Brendan
ractive.get( 'lastname' ); // Eich
```


## Components

You can, of course, declare computed values on components:

```js
Box = Ractive.extend({
  template: boxTemplate,
  computed: { area: '${width} * ${height}' }
});

box = new Box({
  ...,
  data: { width: 20, height: 40 }
});

box.get( 'area' ); // 800
```

Additional computed properties can be declared on the instance:

```js
box2 = new Box({
  ...,
  data: { width: 20, height: 40, depth: 60 },
  computed: { volume: '${area} * ${depth}' }
});

box2.get( 'area' ); // 800
box2.get( 'volume' ); // 48000
```


## Trying it out

As ever, would love to hear any feedback (especially if you encounter bugs etc). You can experiment with this feature by cloning the repo, checking out the `computed` branch, installing dependencies and building the project with grunt:

```shell
$ git clone https://github.com/RactiveJS/Ractive.git
$ cd Ractive
$ git fetch origin
$ git checkout -b computed origin/computed
$ npm i
$ grunt
```

You can then grab the `Ractive.js` from the `build` folder. Alternatively, you can experiment without building the library by using the `sandbox` - copy the `sandbox/sample` folder and edit the `index.html` file.