TheInvisibles/Nerve — pull request #10 by toomim (2014-06-17 06:00:36+00:00)
Title: New subsets api

This implements part of the new subset API.

Here's the spec:
(copy/paste spec from https://github.com/TheInvisibles/Nerve/issues/9#issuecomment-45808396)

Requirements
=========
**Views** must be able to
- Specify which parts of the data they depend upon
- Use a loaded subset, while waiting for a larger set to load

**Cache** must be able to
- Incorporate new subsets of data as they come over the wire
- Store a union of all subsets at all times

**Server** must be able to
- Return partial data
- Specify which parts of the data it's returning

Specification
============

Specifying subsets in URLs/Keys
------
Some views will need only partial data to be loaded. The views will communicate the subset of the data they depend upon by adding a parameter to the REST key:

```javascript
fetch("/proposal/34?subset=summary")
```

If a program only ever uses a single type of subset, it can skip the "=summary" part, and just say "?subset":

```javascript
fetch("/proposal/34?subset")
````

This only works when you don't have to distinguish multiple subset types.

Caching partial data
----
Next, we'll want to mark this information in the cache, so that when a new view needs the data, it knows whether it can use the cached version, or needs to get more data from the server. So we'll record:

```javascript
key = "/proposal/34?subset"
```

Or, if there are multiple subsets used in your app, then you specify a particular one with "?subset=":

```javascript
key = "/proposal/34?subset=summary"
```

If multiple subsets have been loaded, the cache will keep track of them with:

```javascript
key = "/proposal/34?subset=summary,mobile"
```

If there's no subset specified at all, then the entire object is loaded:

```javascript
key = "/proposal/34"
```

**The Union of all Subsets**
The cache wants to maintain a single object for all subsets. So it inserts objects into the cache by ignoring the '?__' part:

```javascript
cache['/proposal'] ==> {key: '/proposal?subset=summary' ...}
```

Over time, it will union them all together:

```javascript
key = "/proposal/34?subset=summary,extended"
```

Rendering loading indicators
------
I don't know the best technique yet, but here are 4 options:

**A Coarse Distinction:** You can write an if statement inside a component's render() function like this:

```javascript
render: function () {
if (this.is_loaded()) {
... render normal stuff ...
} else {
... render loading indicator ...
}
}
```

**Getting Finer:** You can subdivide your render by subsets with this:

```javascript
render: function () {
if (this.is_loaded()) {
... render normal stuff ...
} else if (this.is_loaded('summary') {
... render a partial loading indicator ...
} else {
... render loading indicator ...
}
}
```

**The Dimension of Object:** Each object also has a `is_loaded(subset)` method:
```javascript
render: function () {
// Render the header
if (this.props.proposal.is_loaded('summary')) {
... render normal proposal header ...
} else { header = render_spinner() }

// Render the body
if (this.props.proposal.is_loaded('all')) {
... render normal proposal header ...
} else { body = render_spinner() }
}
```

**The Finest Distinction:** Or just see if fields exist. This might be the simplest technique:
```javascript
render: function () {
var header = section.header || spinner
var page_number = section.page_number || spinner
var body = section.body || spinner
return

{{header}}

{{body}}

{{page_number}}

}
```

**component.is_loaded(subset)**
This function will check the component's required objects have been loaded, or a subset of them if `subset` is specified.

**component.is_loaded(object, subset)**
This will check the objects' key to see if it has the required subset loaded yet. Returns `false` if not. The `subset` label is optional, and defaults to the entire object.