misoproject/d3.chart — pull request #26 by jugglinmike (2013-06-12 21:55:20+00:00)
Title: Abstract data dereferencing

In order to better facilitate reusability, this patch provides an interface for working with chart data. With it, Chart consumers are able to define the "shape" of their data.

This approach should address the concerns raised in issues #12 and #22.

### Usage: Chart Definition

Chart authors *must* specify the data attributes their chart requires through the `dataAttrs` property at Chart definition, i.e.

```javascript
d3.Chart("AwesomeChart", {
dataAttrs: ["time", "space"]
});
```

These attributes (and *only* these attributes) may be accessed from `Chart#transform` and the d3.js selection methods.

```javascript
this.layer("bars").on("enter", function() {
this.attr("height", function(d) {
// Here, d.foo is undefined, even if it exists on
// the data specified to `Chart#draw`
return d.time + d.space;
});
});
```

This behavior is achieved with minimal memory overhead by creating a single object for each data point (but notably not a deep copy of the input data).

### Usage: Chart Consumption

Users of `AwesomeChart` must reference the `dataAttrs` it requests (see above). Any functionality they add to the chart must be in terms of these attributes *regardless of the shape of their data*. If they need to modify how `AwesomeChart` interfaces with their data, they may specify a mapping at Chart initialization time:

```javascript
var myChart = d3.select("body").chart("AwesomeChart", {
// These methods will be invoked in the context of each
// individual data point as the attributes are accessed
dataMapping: {
time: function() { return this.somethingElse; },
space: function() { return this.deeply.nested.attribute; }
}
});

myChart.draw([{
somethingElse: 1371073127051,
deeply: {
nested: {
attribute: 0.5
}
}
}]);
```

This functionality allows the charts to be authored independently of the "shape" of the input data. It satisfies the requirements of CPU efficiency, mandatoriness, and familiarity.

### Potential Concerns

**Browser Compatibility** This approach should be very familiar for those who are used to working with d3.js. Consumer code does not have to utilize a function to dereference data; objects passed to d3.js methods like `attr` and `text` can be de-referenced directed. This requires the use of `Object.defineProperty` and `Object.create`, which [cannot be shimmed in older browsers](https://github.com/kriskowal/es5-shim). It's worth noting that even though [the Aight library](https://github.com/shawnbot/aight) is [recommended on the d3.js wiki](https://github.com/mbostock/d3/wiki#browser-support), it's unclear if d3.js is actually fully functional in IE8 with that shim.

**Memory Efficiency** It is somewhat memory-inefficient in that it creates new objects for each data point, but I think this is a reasonable trade off. In the future, if users want to create high-performance charts that operate over large data sets, we can consider an API to disable this behavior and allow direct data access.