can-connect
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -8,5 +8,7 @@ { | ||
"parent" : "can-connect", | ||
"dest": "./doc" | ||
"minifyBuild": false, | ||
"templates": "docs/theme/templates", | ||
"static": "docs/theme/static" | ||
} | ||
} | ||
} |
129
package.json
{ | ||
"name": "can-connect", | ||
"version": "0.0.2", | ||
"description": "Data connection middleware and utilities", | ||
"main": "can-connect.js", | ||
"dependencies": { | ||
"can": "^2.3.0-pre.1", | ||
"can-set": "^0.2.0", | ||
"when": "^3.7.3" | ||
}, | ||
"devDependencies": { | ||
"documentjs": "^0.3.0-pre.4", | ||
"steal": "^0.10.1", | ||
"steal-qunit": "0.0.2", | ||
"testee": "^0.2.0" | ||
}, | ||
"system": { | ||
"npmIgnore": [ | ||
"devDependencies" | ||
], | ||
"npmDependencies": [ | ||
"steal-qunit" | ||
], | ||
"directories": { | ||
"lib": "src" | ||
}, | ||
"meta": { | ||
"when/es6-shim/Promise": { | ||
"format": "global" | ||
} | ||
} | ||
}, | ||
"scripts": { | ||
"test": "testee --browsers firefox test.html", | ||
"publish": "git push origin --tags", | ||
"release:patch": "npm version patch && npm publish", | ||
"release:minor": "npm version minor && npm publish", | ||
"release:major": "npm version major && npm publish" | ||
}, | ||
"keywords": [ | ||
"CanJS" | ||
], | ||
"author": "Bitovi", | ||
"license": "MIT" | ||
} | ||
"name": "can-connect", | ||
"version": "0.0.3", | ||
"description": "Data connection middleware and utilities", | ||
"main": "can-connect.js", | ||
"dependencies": { | ||
"can": "^2.3.0-pre.1", | ||
"can-set": "^0.2.0", | ||
"when": "^3.7.3" | ||
}, | ||
"devDependencies": { | ||
"documentjs": "^0.3.0-pre.5", | ||
"steal": "^0.10.1", | ||
"steal-qunit": "0.0.2", | ||
"testee": "^0.2.0", | ||
"steal-tools": "^0.10.0" | ||
}, | ||
"docDependencies": { | ||
"steal": "^0.10.1", | ||
"can": "^2.3.0-pre.1", | ||
"can-set": "^0.2.0", | ||
"when": "^3.7.3", | ||
"steal-qunit": "0.0.2" | ||
}, | ||
"system": { | ||
"ignoreBrowser": true, | ||
"npmIgnore": [ | ||
"devDependencies" | ||
], | ||
"npmDependencies": [ | ||
"steal-qunit" | ||
], | ||
"directories": { | ||
"lib": "src" | ||
}, | ||
"meta": { | ||
"when/es6-shim/Promise": { | ||
"format": "global" | ||
} | ||
} | ||
}, | ||
"browser": { | ||
"./all": "./dist/cjs/all", | ||
"./cache-requests/cache-requests": "./dist/cjs/cache-requests/cache-requests", | ||
"./can-connect": "./dist/cjs/can-connect", | ||
"./helpers/helpers": "./dist/cjs/helpers/helpers", | ||
"./helpers/get-items": "./dist/cjs/helpers/get-items", | ||
"./can/map/map": "./dist/cjs/can/map/map", | ||
"./can/can": "./dist/cjs/can/can", | ||
"./can/model/model": "./dist/cjs/can/model/model", | ||
"./data/url/url": "./dist/cjs/data/url/url", | ||
"./helpers/ajax": "./dist/cjs/helpers/ajax", | ||
"./constructor/constructor": "./dist/cjs/constructor/constructor", | ||
"./helpers/weak-reference-map": "./dist/cjs/helpers/weak-reference-map", | ||
"./helpers/overwrite": "./dist/cjs/helpers/overwrite", | ||
"./helpers/id-merge": "./dist/cjs/helpers/id-merge", | ||
"./constructor/store/store": "./dist/cjs/constructor/store/store", | ||
"./helpers/sorted-set-json": "./dist/cjs/helpers/sorted-set-json", | ||
"./data/parse/parse": "./dist/cjs/data/parse/parse", | ||
"./can/super-map/super-map": "./dist/cjs/can/super-map/super-map", | ||
"./constructor/callbacks-once/callbacks-once": "./dist/cjs/constructor/callbacks-once/callbacks-once", | ||
"./data/callbacks/callbacks": "./dist/cjs/data/callbacks/callbacks", | ||
"./data/callbacks-cache/callbacks-cache": "./dist/cjs/data/callbacks-cache/callbacks-cache", | ||
"./data/combine-requests/combine-requests": "./dist/cjs/data/combine-requests/combine-requests", | ||
"./data/inline-cache/inline-cache": "./dist/cjs/data/inline-cache/inline-cache", | ||
"./data/localstorage-cache/localstorage-cache": "./dist/cjs/data/localstorage-cache/localstorage-cache", | ||
"./fall-through-cache/fall-through-cache": "./dist/cjs/fall-through-cache/fall-through-cache", | ||
"./real-time/real-time": "./dist/cjs/real-time/real-time", | ||
"./can/tag/tag": "./dist/cjs/can/tag/tag", | ||
"./data/memory-cache/memory-cache": "./dist/cjs/data/memory-cache/memory-cache" | ||
}, | ||
"scripts": { | ||
"test": "testee --browsers firefox test.html", | ||
"prepublish": "node ./build.js", | ||
"publish": "git push origin --tags", | ||
"release:patch": "npm version patch && npm publish", | ||
"release:minor": "npm version minor && npm publish", | ||
"release:major": "npm version major && npm publish", | ||
"document": "documentjs" | ||
}, | ||
"keywords": [ | ||
"CanJS" | ||
], | ||
"author": "Bitovi", | ||
"license": "MIT" | ||
} |
470
readme.md
@page can-connect | ||
@group can-connect.behaviors 1 Behaviors | ||
@group can-connect.options 2 Options | ||
@group can-connect.externalCRUD 3 Instance Interface | ||
@group can-connect.data_interface 4 Data Interface | ||
@group can-connect.types 5 Data Types | ||
@group can-connect.modules 6 Modules | ||
@group can-connect.modules 2 Modules | ||
@group can-connect.types 3 Data Types | ||
@outline 2 | ||
@@ -119,2 +117,63 @@ # can-connect | ||
This section covers how to install can-connect and then the basics of its use. | ||
### Install | ||
Use npm to install `can-connect`: | ||
``` | ||
> npm install can-connect --save | ||
``` | ||
Then, depending on your module loader, you'll do one of the following: | ||
#### StealJS | ||
Import can-connect and its behaviors like: | ||
``` | ||
import connect from "can-connect"; | ||
import "can-connect/data/url/" | ||
``` | ||
#### Browserify | ||
`require` can-connect and its behaviors like: | ||
``` | ||
var connect = require("can-connect"); | ||
import("can-connect/data/url/url"); | ||
``` | ||
#### AMD | ||
Configure a package to can-connect and its dependency can-set: | ||
``` | ||
require.config({ | ||
packages: [{ | ||
name: 'can-connect', | ||
location: 'node_modules/can-connect/dist/amd', | ||
main: 'can-connect' | ||
},{ | ||
name: 'can-set', | ||
location: 'node_modules/can-connect/node_modules/can-set/dist/amd', | ||
main: 'src/set' | ||
}] | ||
}); | ||
``` | ||
Then use `define` to load can-connect and its behaviors like: | ||
``` | ||
define(["can-connect","can-connect/data/url/url"], function(connect){ | ||
}); | ||
``` | ||
### Basic connection | ||
To use `can-connect`, it's typically best to start out with the most basic | ||
@@ -126,131 +185,362 @@ behaviors: [can-connect/data-url] and [can-connect/constructor]. [can-connect/data-url] | ||
By `typed` data we mean data that is more than just plain JavaScript objects. For | ||
example, we might to create `todo` objects with a `isComplete` method: | ||
``` | ||
var Todo = function(props){ | ||
Object.assign(this, props); | ||
}; | ||
Todo.prototype.isComplete = function(){ | ||
return this.status === "complete"; | ||
}; | ||
``` | ||
And, we might want a special list type with a `completed` and `active` method: | ||
[can-connect/constructor] | ||
``` | ||
var TodoList = function(todos){ | ||
[].push.apply(this, todos); | ||
}; | ||
TodoList.prototype = Object.create(Array.prototype); | ||
TodoList.prototype.completed = function(){ | ||
return this.filter(function(todo){ | ||
return todo.status === "complete"; | ||
}); | ||
}; | ||
TodoList.prototype.active = function(){ | ||
return this.filter(function(todo){ | ||
return todo.status !== "complete"; | ||
}); | ||
}; | ||
``` | ||
```js | ||
import connect from "can-connect"; | ||
import "can-connect/constructor/"; | ||
import "can-connect/data/url/"; | ||
We can create a connection that connects a restful "/api/todos" | ||
service to `Todo` instances and `TodoList` lists like: | ||
var todoConnection = connect( | ||
["constructor","data-url"], | ||
{ | ||
instance: function(data){ new Todo(data) }, | ||
resource: "/services/todos" | ||
}); | ||
``` | ||
var todoConnection = connect(["constructor","data-url"],{ | ||
url: "/api/todos", | ||
list: function(listData, set){ | ||
return new TodoList(listData.data); | ||
}, | ||
instance: function(props) { | ||
return new Todo(props); | ||
} | ||
}); | ||
``` | ||
Next, use the `INSTANCE INTERFACE` methods to create, read, update, and destroy `Todo` instances. | ||
The following gets all todos from the server by making an ajax request to "/services/todos": | ||
And then use that connection to get a `TodoList` of `Todo`s: | ||
``` | ||
todoConnection.findAll({}).then(function(todos){}); | ||
todoConnection.getList({}).then(function(todos){ | ||
var todosEl = document.getElementById("todos-list"); | ||
todosEl.innerHTML = "<h2>Active</h2>"+ | ||
render(todos.active())+ | ||
"<h2>Complete</h2>"+ | ||
render(todos.completed()); | ||
}); | ||
var render = function(todos) { | ||
return "<ul>"+todos.map(function(todo){ | ||
return "<li>"+todo.name+ | ||
"<input type='checkbox' "+ | ||
(todo.isComplete() ? "checked" : "")+"/></li>"; | ||
}).join("")+"</ul>"; | ||
}; | ||
``` | ||
`todos` is an array of `Todo` instances. | ||
The following demo shows the result: | ||
@demo can-connect/docs/demos/basics.html | ||
This connection also lets you create, update, and destroy a Todo instance as follows: | ||
``` | ||
var todo = new Todo({ | ||
name: "take out trash" | ||
}) | ||
Then, use that `todoConnection`'s `External CRUD Methods` to load data: | ||
// POSTs to /api/todos name=take out trash | ||
// server returns {id: 5} | ||
todoConnection.save( todo ).then(function(todo){ | ||
todo.id //-> 5 | ||
todo.name = 'take out garbage' | ||
// PUTs to /api/todos/5 name=take out garbage | ||
// server returns {id: 5, "take out garbage"} | ||
todoConnection.save( todo ).then( function(todo){ | ||
// DELETEs to /api/todos/5 | ||
// serer returns {} | ||
todoConnection.destroy( todo ).then( function(todo){ | ||
}); | ||
}); | ||
}); | ||
``` | ||
```js | ||
todoConnection.findAll({completed: true}).then(function(todos){ | ||
### Configure behaviors | ||
Whenever `connect` creates a connection, it always adds the [connect.base] | ||
behavior. This behavior defines configurable options that are used by almost | ||
every other behavior. For example, if your data uses an `_id` property | ||
to uniquely identify todos, you | ||
can specify this with [connect.base.idProp] like: | ||
``` | ||
var todoConnection = connect(["constructor","data-url"],{ | ||
url: "/api/todos", | ||
idProp: "_id" | ||
}); | ||
``` | ||
To work well with `can-connect` your code might need to call some of its hooks: | ||
Other behaviors list their configurable options in their own docs page. | ||
```js | ||
Todo.prototype.bind = function(){ | ||
// tells this todo to be saved in the store so no other | ||
// instances that have its ID will be created | ||
todoConnection.observedInstance(this); | ||
### Overwrite behaviors | ||
If configurable options are not enough, you can overwrite any behavior with your own behavior. | ||
For example, the `constructors` [can-connect/constructor.updatedInstance] behavior | ||
sets the instance's properties to match the result of [connection.updateData]. But if | ||
the `PUT /api/todos/5 name=take out garbage` request returned `{}`, the following would result in | ||
a todo with only an `id` property: | ||
``` | ||
var todo = new Todo({id: 5, name: "take out garbage"}) | ||
// PUTs to /api/todos/5 name=take out garbage | ||
// server returns {} | ||
todoConnection.save( todo ).then( function(todo){ | ||
todo.id //-> 5 | ||
todo.name //-> undefined | ||
}); | ||
``` | ||
The following overwrites the behavior of `updateData`: | ||
``` | ||
var mergeDataBehavior = { | ||
updateData: function(instance, data){ | ||
Object.assign(instance, data); | ||
} | ||
} | ||
var todoConnection = connect([ | ||
"constructor", | ||
"data-url", | ||
mergeDataBehavior | ||
],{ | ||
url: "/api/todos" | ||
}); | ||
``` | ||
You can add your own behavior that overwrite all base behaviors by adding | ||
it to the end of the behaviors list. | ||
### CanJS use | ||
If you are using CanJS, you can either: | ||
- use the [can-connect/can/map] behavior that overwrites | ||
many methods and settings to work with `can.Map` and `can.List`. | ||
- use the [can-connect/can/super-map] helper to create a connection that bundles "can/map" and | ||
many of the other extensions. | ||
Using [can-connect/can/map] to create a connection looks like: | ||
``` | ||
var Todo = can.Map.extend({ ... }); | ||
Todo.List = can.List.extend({Map: Todo},{}); | ||
var todoConnection = connect([ | ||
"data-url", | ||
"can/map", | ||
"constructor", | ||
"constructor-store" | ||
],{ | ||
Map: Todo, | ||
url: "/todos" | ||
}); | ||
``` | ||
When you bind on a `Todo` instance or `Todo.List` list, they will automatically call | ||
[can.connect/constructor-store.addInstanceReference] and [can.connect/constructor-store.addListReference]. | ||
Using [can-connect/can/super-map] to create a connection looks like: | ||
``` | ||
var Todo = can.Map.extend({ ... }); | ||
Todo.List = can.List.extend({Map: Todo},{}); | ||
var todoConnection = superMap({ | ||
Map: Todo, | ||
url: "/todos" | ||
}); | ||
``` | ||
### ReactJS use | ||
Help us create a special ReactJS behavior that integrates | ||
a connection with React's observable life-cycle. Read more [here](#section_Otheruse). | ||
### Angular use | ||
Help us create a special AngularJS behavior that integrates | ||
a connection with Angular's observable life-cycle. Read more [here](#section_Otheruse). | ||
### Backbone use | ||
Help us create a special BackboneJS behavior that integrates | ||
a connection with Backbone's observable life-cycle. Read more [here](#section_Otheruse). | ||
### Other use | ||
Integrating `can-connect` with your framework is typically pretty easy. In general, | ||
the pattern involves creating a behavior that integrates with your framework's | ||
observable instances. The [can-connect/can/map] | ||
behavior can serve as a good guide. You'll typically want to implement the following | ||
in your behavior: | ||
`.instance` - Creates the appropriate observable object type. | ||
`.list` - Creates the appropriate observable array type. | ||
`.serializeInstance` - Return a plain object out of the observable object type. | ||
`.serializeList` - Return a plain array out of the observable array type. | ||
`.createdInstance` - Update an instance with data returned from `createData`. | ||
`.updatedInstance` - Update an instance with data returned from `updateData`. | ||
`.destroyedInstance` - Update an instance with data returned from `destroyData`. | ||
`.updatedList` - Update a list with raw data. | ||
And, in most frameworks you know when a particular observable is being used, typically | ||
observed, and when it can be discarded. In those places, you should call: | ||
[can.connect/constructor-store.addInstanceReference] - Call when an instance is being used. | ||
[can.connect/constructor-store.deleteInstanceReference] - Call when an instance is no longer being used. | ||
[can.connect/constructor-store.addListReference] - Call when a list is being used. | ||
[can.connect/constructor-store.deleteListReference] - Called when a list is no longer being used. | ||
## Interfaces | ||
The API is broken up into: | ||
The following is a list of the most important interface methods and properties implemented | ||
or consumed by the core behaviors. | ||
- The different behaviors, like perist, instance-store, etc | ||
- A full list of options that behaviors consume. | ||
- [The core list of hooks that behaviors can call or implement](#core-hooks) | ||
- How to create a behavior. | ||
### Identifiers | ||
`.id( props | instance ) -> String` - Returns a unique identifier for the instance or raw data. | ||
`.idProp -> String="id"` - The name of the unique identifier property. | ||
`.listSet(list) -> set` - Returns the set a list represents. | ||
`.listSetProp -> String="__set"` - The property on a List that contains its set. | ||
## Core Hooks | ||
Implemented by [connect.base]. | ||
These are hooks that most plugins will use in some way. | ||
### Instance Interface | ||
### External Persisted CRUD methods | ||
The following methods operate on instances and lists. | ||
The methods that people using an implemented connection should use. | ||
#### CRUD methods: | ||
- [connection.getList] - load instances | ||
- [connection.get] - load a single instance | ||
- [connection.save] - creates or updates an instance | ||
- [connection.destroy] - destroys an instance | ||
`.getList(set) -> Promise<List>` - retrieve a list of instances. | ||
`.getList(set) -> Promise<Instance>` - retrieve a single instance. | ||
`.save(instance) -> Promise<Instance>` - creates or updates an instance. | ||
`.destroy(instance) -> Promise<Instance>` - destroys an instance. | ||
Implemented by [can-connect/constructor]. Overwritten by [can-connect/constructor/store]. | ||
#### Instance callbacks | ||
`.createdInstance(instance, props)` - An instance is created. | ||
`.updatedInstance(instance, props)` - An instance is updated. | ||
`.destroyedInstance(instance, props)` - An instance is destroyed. | ||
`.updatedList(list, updatedListData, set)` - A list has been updated. | ||
Implemented by [can-connect/constructor]. Overwritten by [data-connect/real-time], | ||
[can-connect/constructor/callbacks-once]. | ||
#### Hydrators and Serializers | ||
`.instance(props) -> Instance` - Creates an instance given raw data. | ||
`.list({data: Array<Instance>}) -> List` - Creates a list given an array of instances. | ||
`.hydrateInstance(props) -> Instance` - Provides an instance given raw data. | ||
`.hydrateList({ListData}, set) -> List` - Provides a list given raw data. | ||
`.hydratedInstance(instance)` - Called whenever an instance is created in memory. | ||
`.hydratedList(list, set)` - Called whenever a list is created in memory. | ||
`.serializeInstance(instance) -> Object` - Returns the serialized form of an instance. | ||
`.serializeList(list) -> Array<Object>` - Returns the serialized form of a list and its instances. | ||
Implemented by [can-connect/constructor]. Overwritten by [can-connect/constructor/store], | ||
[can-connect/fall-through-cache]. | ||
### Data Interface | ||
The raw-data connection methods. These are used by "Instance Interface". These should | ||
be implemented by behaviors. | ||
The raw-data connection methods. | ||
- [connection.getListData] - Retrieves list data for a particular set. | ||
- [connection.updateListData] - Called when a set of data is updated with the raw data to be | ||
saved. This is normally used for caching connection layers. | ||
- [connection.createData] - Creates instance data given the serialized form of the data. | ||
Returns any additional properties that should be added to the instance. A client ID is passed of the instance that is being created. | ||
- [connection.updateData] - Updates instance data given the serialized form of the data. Returns any additional properties that should be added to the instance. | ||
- [connection.destroyData] - Destroys an instance given the seralized form of the data. Returns any additional properties that should be added to the instance. | ||
- `parseListData(*) -> {data:Array<Object>}` - Given the response of getListData, return the right object format. | ||
- `getData(set) -> Promise<Object>` - Retrieves data for a particular item. | ||
- `parseInstanceData(*) -> Object` - Given a single items response data, return the right object format. This is called by parseListData as well as all other internal CRUD methods. | ||
#### CRUD methods | ||
## Hooks to update raw data | ||
`.getListData(set) -> Promise<ListData>` - Retrieves list data. | ||
`.updateListData(listData[, set]) -> Promise<ListData>` - Update a list's data. | ||
`.getSets() -> Promise<Array<Set>>` - Returns the sets available to the connection. | ||
These methods are used to update data from the outside, usually by a real time connection. | ||
- `createInstance( props ) -> instance` | ||
- `updateInstance( props ) -> instance` | ||
- `destroyInstance( props ) -> instance` | ||
`.getData(params) -> Promise<Object>` - Retrieves data for a particular item. | ||
`.createData(props, cid) -> Promise<props>` - Creates instance data given the serialized form of the data. | ||
A client ID is passed of the | ||
instance that is being created. | ||
`.updateData(props) -> Promise<props>` - Updates instance data given the | ||
serialized form of the data. | ||
`.destroyData(props) -> Promise<props>` - Destroys an instance given the seralized | ||
form of the data. | ||
### Instance and Instances | ||
`.clear() -> Promise` - Clears all data in the connection. | ||
- `hydrateInstance( props )` - Creates an instance in memory given data for that instance. | ||
- `hydrateList({data: Array<Object>}, set)` - Creates a container for instances and all the instances within that container. | ||
- `createdInstance(instance, props)` - Called whenever an instance is created in the persisted state. | ||
- `updatedInstance(instance, props)` - Called whenever an instance is updated in the persisted state. | ||
- `destroyedInstance(instance, props)` - Called whenever an instance is destroyed in the persisted state. | ||
- `updatedList(list, updatedListData, set)` - Called whenever a list has been updated. `updatedList` should be merged into `list`. | ||
- `serializeInstance` | ||
- `serializeList` | ||
Implemented by [can-connect/data-url], | ||
[can-connect/data/localstorage-cache], [can-connect/data/memory-cache]. | ||
Overwritten by [can-connect/cache-requests], [can-connect/data/combine-requests], | ||
[can-connect/data/inline-cache], [can-connect/fall-through-cache]. | ||
Consumed by [can-connect/constructor]. | ||
### Identifiers | ||
#### Data Callbacks | ||
- `id( props | instance ) -> STRING` - Given the raw data for an instance, or the instance, returns a unique identifier for the instance. | ||
- `idProp {String="id"}` - The name of the unique identifier property. | ||
- `listSet(list) -> set` - Returns the set this set represents. | ||
- `listSetProp {String="__set"}` - The property on a List that contains its set. | ||
`.gotListData(listData, set) -> ListaData` - List data is retrieved. | ||
`.gotData( props, params) -> props` - Instance data is retreived. | ||
`.createdData( props, params, cid) -> props` - An instance's data is created. | ||
`.updatedData( props, params) -> props` - An instance's data is updated. | ||
`.destroyedData( props, params) -> props` - An instance's data is destroyed. | ||
## External Hooks | ||
Implemented by [can-connect/data/callbacks]. Overwritten by [can-connect/data/callbacks-cache], | ||
[can-connect/real-time]. | ||
Hooks that your library and code should be calling. | ||
#### Response parsers | ||
- `hydratedInstance(instance)` - Called whenever an isntance is created in memory. | ||
- `hydratedInstance(list, set)` - Called whenever a list is created in memory. | ||
`.parseListData(*) -> ListData` - Given the response of getListData, return the right object format. | ||
`.parseInstanceData(*) -> props` - Given the response of getData, createData, updateData, and destroyData, | ||
return the right object format. | ||
- `addInstanceReference(instance)` - Called whenver an instance is observed. This serves as a signal that memory-unsafe actions can be performed. | ||
- `deleteInstanceReference(instance)` - Called whenever an instance is no longer observed. This serves as a signal that memory-unsafe should be removed. | ||
- `addListReference(list)` - Called whenever a a list is observed. | ||
- `deleteListReference(list)` - Called whenever a a list is unobserved. | ||
Implemented by [can-connect/data-parse]. | ||
#### Store Interface | ||
`.addInstanceReference(instance)` - Signal that memory-unsafe actions can be performed on the instance. | ||
`.deleteInstanceReference(instance)` - Signal that memory-unsafe actions should be removed. | ||
`.addListReference(list)` - Signal that memory-unsafe actions can be performed on the list. | ||
`.deleteListReference(list)` - Signal that memory-unsafe actions should be removed. | ||
Implemented by [can-connect/constructor/store]. | ||
#### Real-time Methods | ||
`createInstance( props ) -> Promise<instance>` - Inform the connection an instnace has been created. | ||
`updateInstance( props ) -> Promise<instance>` - Inform the connection an instnace has been updated. | ||
`destroyInstance( props ) -> Promise<instance>` - Inform the connection an instnace has been destroyed. | ||
Implemented by [can-connect/real-time]. | ||
## Creating Behaviors | ||
@@ -262,3 +552,3 @@ | ||
```js | ||
connect.behavior("my-behavior", function(baseBehavior, options){ | ||
connect.behavior("my-behavior", function(baseBehavior){ | ||
return { | ||
@@ -270,11 +560,11 @@ // Hooks here | ||
For example, localStorage might looks like: | ||
For example, creating a simple localStorage behavior might looks like: | ||
```js | ||
connect.behavior("localstorage", function(baseBehavior, options){ | ||
connect.behavior("localstorage", function(baseBehavior){ | ||
return { | ||
getData: function(params){ | ||
var id = this.idProp; | ||
var id = this.id(params); | ||
return new Promise(function(resolve){ | ||
var data = localStorage.getItem(options.name+"/"+params[id]); | ||
var data = localStorage.getItem(baseBehavior.name+"/"+id); | ||
resolve( JSON.parse(data) ) | ||
@@ -284,8 +574,10 @@ }); | ||
createData: function(props){ | ||
var nextId = ++JSON.parse( localStorage.getItem(options.name+"-ID") || "0"); | ||
localStorage.setItem(options.name+"-ID"), nextId); | ||
var id = localStorage.getItem(baseBehavior.name+"-ID") || "0"; | ||
var nextId = ++JSON.parse( id ); | ||
localStorage.setItem(baseBehavior.name+"-ID"), nextId); | ||
var id = this.idProp; | ||
return new Promise(function(resolve){ | ||
props[id] = nextId; | ||
localStorage.setItem(options.name+"/"+nextId, props); | ||
localStorage.setItem(baseBehavior.name+"/"+nextId, props); | ||
resolve( props ) | ||
@@ -292,0 +584,0 @@ }); |
@@ -19,3 +19,3 @@ | ||
* Overwrites [can-connect/cache-requests.getListData] to use set logic to | ||
* determine which data is already in [connection.cacheConnection] or needs to be loaded from the base connection. | ||
* determine which data is already in [connect.base.cacheConnection] or needs to be loaded from the base connection. | ||
* | ||
@@ -130,3 +130,3 @@ * It then gets data from the cache and/or the base connection, merges it, and returns it. | ||
* @param {Array<Set>} availableSets An array of available sets in the | ||
* [connection.cacheConnection]. | ||
* [connect.base.cacheConnection]. | ||
* @return {Promise<{needs: Set, cached: Set}>} | ||
@@ -187,3 +187,3 @@ * | ||
* @param {can-connect.listData} neededData The data loaded from the base connection. | ||
* @param {can-connect.listData} cachedData The data loaded from the [connection.cacheConnection]. | ||
* @param {can-connect.listData} cachedData The data loaded from the [connect.base.cacheConnection]. | ||
* | ||
@@ -200,7 +200,7 @@ * @return {can-connect.listData} Return the merged cached and requested data. | ||
* | ||
* Only request data that hasn't already been loaded by [connection.cacheConnection]. | ||
* Only request data that hasn't already been loaded by [connect.base.cacheConnection]. | ||
* | ||
* @signature `connection.getListData(set)` | ||
* | ||
* Overwrites a base connection's `getListData` to use data in the [connection.cacheConnection] | ||
* Overwrites a base connection's `getListData` to use data in the [connect.base.cacheConnection] | ||
* whenever possible. This works by [connection.getSets getting the stored sets] and doing a | ||
@@ -207,0 +207,0 @@ * [can-connect/cache-requests.getDiff diff] to see what should be loaded from the cache |
@@ -52,3 +52,7 @@ var helpers = require("./helpers/"); | ||
"constructor","constructor-store","can-map", | ||
"fall-through-cache","data-inline-cache","data-callbacks-cache","data-callbacks","constructor-callbacks-once" | ||
"fall-through-cache","data-inline-cache", | ||
"data-worker", | ||
"data-callbacks-cache","data-callbacks","constructor-callbacks-once" | ||
]; | ||
@@ -67,3 +71,4 @@ | ||
var newBehavior = new Behavior; | ||
var res = behavior.apply(newBehavior, arguments); | ||
// allows behaviors to be a simple object, not always a function | ||
var res = typeof behavior === "function" ? behavior.apply(newBehavior, arguments) : behavior; | ||
helpers.extend(newBehavior, res); | ||
@@ -83,13 +88,202 @@ newBehavior.__behaviorName = name; | ||
/** | ||
* @function {Behavior} connect.base base | ||
* @parent can-connect.behaviors | ||
* | ||
* The base behavior added to every `connect` behavior. | ||
* | ||
* @signature `base(options)` | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* The `"base"` behavior is added automatically to every connection created by `connect`. So even we do: | ||
* | ||
* ``` | ||
* var connection = connect([],{}); | ||
* ``` | ||
* | ||
* The connection still has `"base"` functionality: | ||
* | ||
* ``` | ||
* connection.id({id: 1}) //-> 1 | ||
* ``` | ||
*/ | ||
var core = connect.behavior("base",function(base){ | ||
return { | ||
/** | ||
* @function connect.base.id id | ||
* @parent connect.base | ||
* | ||
* Uniquely identify an instance or raw instance data. | ||
* | ||
* @signature `connection.id(instance)` | ||
* | ||
* Returns the [connect.base.idProp] if it exists, otherwise the `id` property. | ||
* | ||
* @param {Instance|Object} instance The instance or raw `props` for an instance. | ||
* | ||
* @return {String|Number} A string or number uniquely representing `instance`. | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* Many extensions, such as the [can-connect/constructor/store], need | ||
* to have a unique identifier for an instance or instance data. The | ||
* `connection.id` method should return that. | ||
* | ||
* Typically, an item's `id` is a simply propertly value on the object. | ||
* For example, `todo` data might look like: | ||
* | ||
* ``` | ||
* {_id: 5, name: "do the dishes"} | ||
* ``` | ||
* | ||
* In this case [connect.base.idProp] should be set to `"_id"`. However, | ||
* some data sources have compound ids. For example, "Class Assignment" | ||
* connection might be represented by two properties, the `studentId` and the | ||
* `classId`. For this kind of setup, you can provide your own id function as | ||
* follows: | ||
* | ||
* ``` | ||
* var overwrites = { | ||
* id: function(classAssignment){ | ||
* return classAssignment.studentId+"-"+classAssignment.classId; | ||
* } | ||
* }; | ||
* var classAssignmentConnection = connect(['data-url', overwrites],{ | ||
* url: "/class_assignments" | ||
* }); | ||
* ``` | ||
* | ||
*/ | ||
id: function(instance){ | ||
return instance[this.idProp || "id"]; | ||
}, | ||
/** | ||
* @property {String} connect.base.idProp idProp | ||
* @parent connect.base | ||
* | ||
* Specifies the property that uniquely identifies an instance. | ||
* | ||
* @option {String} The name of the property that uniquely identifies | ||
* an instance. Defaults to `"id"`. | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* ``` | ||
* connect(["data-url"],{ | ||
* idProp: "_id" | ||
* }); | ||
* ``` | ||
* | ||
*/ | ||
idProp: base.idProp || "id", | ||
/** | ||
* @function connect.base.listSet listSet | ||
* @parent connect.base | ||
* | ||
* Uniquely identify the set a list represents. | ||
* | ||
* @signature `connection.listSet(list)` | ||
* | ||
* Returns the [connect.base.listSetProp] if it exists. | ||
* | ||
* @param {List} list A list instance. | ||
* | ||
* @return {Object} An object that can be passed to `JSON.stringify` | ||
* to represent the list. | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* Many extensions, such as the [can-connect/constructor/store], need | ||
* to have a unique identifier for a list. The | ||
* `connection.listSet` method should return that. | ||
* | ||
* Typically, an item's `set` is an expando property added to | ||
* a list. For example, a list of todos might looks like todos | ||
* after the following has run: | ||
* | ||
* ``` | ||
* var todos = [{_id: 5, name: "do the dishes"}] | ||
* todos.set = {due: 'today'}; | ||
* ``` | ||
* | ||
* In this case [connect.base.listSetProp] should be set to `"set"`. | ||
* | ||
*/ | ||
listSet: function(list){ | ||
return list[this.listSetProp]; | ||
}, | ||
/** | ||
* @property {String} connect.base.listSetProp listSetProp | ||
* @parent connect.base | ||
* | ||
* Specifies the property that uniquely identifies a list. | ||
* | ||
* @option {String} The name of the property that uniquely identifies | ||
* the list. Defaults to `"__set"`. | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* ``` | ||
* connect(["data-url"],{ | ||
* listSetProp: "set" | ||
* }); | ||
* ``` | ||
* | ||
*/ | ||
listSetProp: "__set", | ||
init: function(){} | ||
/** | ||
* @property {set.Algebra} connect.base.algebra algebra | ||
* @parent connect.base | ||
* | ||
* @option {set.Algebra} A [set algebra](https://github.com/canjs/can-set#setalgebra) that is used by | ||
* many behaviors to compare the `set` objects passed to | ||
* [connection.getListData] and [connection.getList]. By | ||
* default no algebra is provided. | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* ``` | ||
* var algebra = new set.Algebra(set.comparators.range("start","end")); | ||
* | ||
* connect([...behavior names...],{ | ||
* algebra: algebra | ||
* }); | ||
* ``` | ||
*/ | ||
/** | ||
* @property {connection} connect.base.cacheConnection cacheConnection | ||
* @parent connect.base | ||
* | ||
* A connection used for caching. | ||
* | ||
* @option {connection} A connection that can be used for | ||
* "Data Interface" requests. Several behaviors | ||
* look for this property. By `cacheConnection` is null. | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* ``` | ||
* var cacheConnection = connect(['memory-cache'],{}) | ||
* | ||
* connect([...behavior names...],{ | ||
* cacheConnection: cacheConnection | ||
* }); | ||
* ``` | ||
*/ | ||
}; | ||
@@ -96,0 +290,0 @@ }); |
@@ -34,2 +34,5 @@ require("when/es6-shim/Promise"); | ||
init: function(){ | ||
this.Map = this.Map || Map.extend({}); | ||
this.List = this.List || List.extend({}); | ||
overwrite(this, this.Map, mapOverwrites, mapStaticOverwrites); | ||
@@ -47,3 +50,3 @@ overwrite(this, this.List, listPrototypeOverwrites, listStaticOverwrites); | ||
* | ||
* Reads [connection.idProp] so that it's observable unless | ||
* Reads [connect.base.idProp] so that it's observable unless | ||
* the id is being read as part of the map being bound or | ||
@@ -390,3 +393,3 @@ * unbound. | ||
* | ||
* Returns `true` if [connection.id] is 0 or truthy. | ||
* Returns `true` if [connect.base.id] is 0 or truthy. | ||
* | ||
@@ -393,0 +396,0 @@ * @return {Boolean} |
@@ -5,2 +5,18 @@ /** | ||
* Makes a constructor function work just like can.Model | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* ``` | ||
* import Model from "can-connect/can/model/"; | ||
* | ||
* Todo = Model.extend({ | ||
* findAll: "/todos" | ||
* },{}); | ||
* | ||
* Todo.findAll({}).then(function(todos){ | ||
* | ||
* }); | ||
* ``` | ||
*/ | ||
@@ -7,0 +23,0 @@ |
/** | ||
* @module can-connect/can/super-map can/super-map | ||
* @module {function} can-connect/can/super-map can/super-map | ||
* @parent can-connect.modules | ||
* | ||
* Create connection with many of the best behaviors in can-connect and hook it up to a map. | ||
* | ||
* @signature `superMap(options)` | ||
* | ||
* Creates a connection with the following behaviors: [can-connect/constructor], | ||
* [can-connect/can/map], | ||
* [can-connect/constructor/store], | ||
* [can-connect/data/callbacks], | ||
* [can-connect/data/callbacks-cache], | ||
* [can-connect/data/combine-requests], | ||
* [can-connect/data/inline-cache], | ||
* [can-connect/data-parse], | ||
* [can-connect/data-url], | ||
* [can-connect/real-time], | ||
* [can-connect/constructor/callbacks-once]. | ||
* | ||
* And creates a [can-connect/data/localstorage-cache] to use as a [connect.base.cacheConnection]. | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* ``` | ||
* var Todo = can.Map.extend({ ... }); | ||
* TodoList = can.List.extend({Map: Todo},{ ... }); | ||
* | ||
* | ||
* var todoConnection = superMap({ | ||
* idProp: "_id", | ||
* Map: Todo, | ||
* List: TodoList, | ||
* url: "/services/todos" | ||
* }); | ||
* ``` | ||
*/ | ||
@@ -44,3 +79,4 @@ var connect = require("can-connect"); | ||
name: options.name+"Cache", | ||
idProp: options.idProp | ||
idProp: options.idProp, | ||
algebra: options.algebra | ||
}); | ||
@@ -47,0 +83,0 @@ behaviors.push("fall-through-cache"); |
@@ -9,2 +9,6 @@ /** | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* ``` | ||
@@ -15,7 +19,11 @@ * connect.tag("order-model", connection); | ||
* ``` | ||
* <order-model getList="{type=orderType}"> | ||
* {{#isPending}}Loading{{/isPending}} | ||
* {{#isResolved}} | ||
* Data: {{value}} | ||
* {{/isResolved}} | ||
* <order-model get-list="{type=orderType}"> | ||
* <ul> | ||
* \{{#isPending}}<li>Loading</li>\{{/isPending}} | ||
* \{{#isResolved}} | ||
* \{{#each value}} | ||
* <li>\{{name}}</li> | ||
* \{{/each}} | ||
* \{{/isResolved}} | ||
* </ul> | ||
* </order-model> | ||
@@ -22,0 +30,0 @@ * ``` |
@@ -5,7 +5,11 @@ /** | ||
* | ||
* Glues the result of the raw `Data Interface` methods to callbacks. This is | ||
* useful if you want something to happen with raw data anytime raw data is requested | ||
* or manipulated. | ||
* Unecessary calls to the instance callback methods. | ||
* | ||
* @signature `constructorCallbacksOnce(baseBehavior)` | ||
* | ||
* Prevents duplicate calls to the instance callback methods by tracking | ||
* the last data the methods were called with. If called with the | ||
* same data again, it does not call the base behavior's instance callback. | ||
* | ||
* | ||
*/ | ||
@@ -18,4 +22,4 @@ var connect = require("can-connect"); | ||
/** | ||
* @function can-connect/data/callbacks.createdData createdData | ||
* @parent can-connect/data/callbacks | ||
* @function can-connect/constructor/callbacks-once.createdData createdData | ||
* @parent can-connect/constructor/callbacks-once | ||
* | ||
@@ -28,4 +32,4 @@ * Called with the resolved response data | ||
/** | ||
* @function can-connect/data/callbacks.updatedData updatedData | ||
* @parent can-connect/data/callbacks | ||
* @function can-connect/constructor/callbacks-once.updatedData updatedData | ||
* @parent can-connect/constructor/callbacks-once | ||
* | ||
@@ -38,4 +42,4 @@ * Called with the resolved response data | ||
/** | ||
* @function can-connect/data/callbacks.destroyedData destroyedData | ||
* @parent can-connect/data/callbacks | ||
* @function can-connect/constructor/callbacks-once.destroyedData destroyedData | ||
* @parent can-connect/constructor/callbacks-once | ||
* | ||
@@ -42,0 +46,0 @@ * Called with the resolved response data |
@@ -240,3 +240,3 @@ /** | ||
* | ||
* Checks if the instance has an [connection.id] or not. If it | ||
* Checks if the instance has an [connect.base.id] or not. If it | ||
* has an id, the instance will be updated; otherwise, it will be created. | ||
@@ -470,3 +470,3 @@ * | ||
* Sets the properties in `instance` to match the properties and values in `props` | ||
* with the exception of [connection.idProp], which it leaves alone. | ||
* with the exception of [connect.base.idProp], which it leaves alone. | ||
* | ||
@@ -517,3 +517,3 @@ * @param {Instance} instance The instance to update. | ||
* Sets the properties in `instance` to match the properties and values in `props` | ||
* with the exception of [connection.idProp], which it leaves alone. | ||
* with the exception of [connect.base.idProp], which it leaves alone. | ||
* | ||
@@ -520,0 +520,0 @@ * @param {Instance} instance The instance to update. |
@@ -28,7 +28,7 @@ /** | ||
* - provide a store of instances and lists used by the client. | ||
* - prevent multiple instances from being hydrated for the same [connection.id] or multiple | ||
* lists for the same [connection.listSet]. | ||
* - prevent multiple instances from being hydrated for the same [connect.base.id] or multiple | ||
* lists for the same [connect.base.listSet]. | ||
* | ||
* The stores provide access to an instance | ||
* by its [connection.id] or a list by its [connection.listSet]. These stores are | ||
* by its [connect.base.id] or a list by its [connect.base.listSet]. These stores are | ||
* used by other extensions like [can-connect/real-time] and [can-connect/fall-through-cache]. | ||
@@ -40,3 +40,3 @@ * | ||
* | ||
* @demo src/constructor/store/store.html | ||
* @demo can-connect/src/constructor/store/store.html | ||
* | ||
@@ -94,3 +94,3 @@ * You'll notice that you can edit one todo's name and the other | ||
* | ||
* A store of instances mapped by [connection.id]. | ||
* A store of instances mapped by [connect.base.id]. | ||
*/ | ||
@@ -101,3 +101,3 @@ instanceStore: new WeakReferenceMap(), | ||
* @parent can.connect/constructor-store.stores | ||
* A store of lists mapped by [connection.listSet]. | ||
* A store of lists mapped by [connect.base.listSet]. | ||
*/ | ||
@@ -129,3 +129,3 @@ listStore: new WeakReferenceMap(), | ||
* | ||
* Adds a reference to an instance in the [can.connect/constructor-store.instanceStore] by [connection.id]. | ||
* Adds a reference to an instance in the [can.connect/constructor-store.instanceStore] by [connect.base.id]. | ||
* The number of references are incremented. | ||
@@ -140,3 +140,3 @@ * | ||
* The [can.connect/constructor-store.instanceStore] contains a collection of instances | ||
* created for each [connection.id]. The `instanceStore` is used to prevent creating the | ||
* created for each [connect.base.id]. The `instanceStore` is used to prevent creating the | ||
* same instance multiple times. Instances need to be added to this store for this behavior | ||
@@ -211,3 +211,3 @@ * to happen. To do this, call `addInstanceReference` like the following: | ||
* | ||
* Removes a reference to an instance by [connection.id] so it can be garbage collected. | ||
* Removes a reference to an instance by [connect.base.id] so it can be garbage collected. | ||
* | ||
@@ -255,3 +255,3 @@ * @signature `connection.addInstanceReference( instance )` | ||
* | ||
* @param {Set} [set] The set this list represents if it can't be identified with [connection.listSet]. | ||
* @param {Set} [set] The set this list represents if it can't be identified with [connect.base.listSet]. | ||
* | ||
@@ -263,3 +263,3 @@ * @body | ||
* The [can.connect/constructor-store.listStore] contains a collection of lists | ||
* created for each [connection.listSet]. The `listStore` is used to prevent creating the | ||
* created for each [connect.base.listSet]. The `listStore` is used to prevent creating the | ||
* same list multiple times and for identifying a list for a given set. Lists need to be added to this store for this behavior | ||
@@ -317,3 +317,3 @@ * to happen. To do this, call `addListReference` like the following: | ||
* | ||
* Removes a reference to a list by [connection.listSet] so it can be garbage collected. | ||
* Removes a reference to a list by [connect.base.listSet] so it can be garbage collected. | ||
* | ||
@@ -457,3 +457,3 @@ * @signature `connection.addInstanceReference( instance )` | ||
/** | ||
* @fuction can.connect/constructor-store.getList getList | ||
* @function can.connect/constructor-store.getList getList | ||
* @parent can.connect/constructor-store.crud | ||
@@ -460,0 +460,0 @@ * |
@@ -5,5 +5,5 @@ /** | ||
* | ||
* Calls [connection.cacheConnection] methods whenever | ||
* Calls [connect.base.cacheConnection] methods whenever | ||
* the [can-connect/data/callbacks data interface callbacks] are called. This is | ||
* useful for making sure a [connection.cacheConnection] is updated whenever data is updated. | ||
* useful for making sure a [connect.base.cacheConnection] is updated whenever data is updated. | ||
*/ | ||
@@ -21,3 +21,3 @@ var connect = require("can-connect"); | ||
* Called with the resolved response data | ||
* of [connection.createData]. Calls `createData` on the [connection.cacheConnection]. | ||
* of [connection.createData]. Calls `createData` on the [connect.base.cacheConnection]. | ||
*/ | ||
@@ -30,3 +30,3 @@ createdData: "createData", | ||
* Called with the resolved response data | ||
* of [connection.updateData]. Calls `updateData` on the [connection.cacheConnection]. | ||
* of [connection.updateData]. Calls `updateData` on the [connect.base.cacheConnection]. | ||
*/ | ||
@@ -39,3 +39,3 @@ updatedData: "updateData", | ||
* Called with the resolved response data | ||
* of [connection.destroyData]. Calls `destroyData` on the [connection.cacheConnection]. | ||
* of [connection.destroyData]. Calls `destroyData` on the [connect.base.cacheConnection]. | ||
*/ | ||
@@ -42,0 +42,0 @@ destroyedData: "destroyData" |
@@ -174,3 +174,3 @@ | ||
* | ||
* This implementation uses [can-set's getSubset](https://github.com/canjs/can-set#setgetsubset) with [connection.algebra]. | ||
* This implementation uses [can-set's getSubset](https://github.com/canjs/can-set#setgetsubset) with [connect.base.algebra]. | ||
* | ||
@@ -177,0 +177,0 @@ * @param {Set} set the subset initially requested |
@@ -66,3 +66,3 @@ var connect = require("can-connect"); | ||
* cached data for that connection. That inner object is a mapping | ||
* between either [connection.id ids] or [connection.listSet serialized sets] to | ||
* between either [connect.base.id ids] or [connect.base.listSet serialized sets] to | ||
* response data for those requests. | ||
@@ -129,3 +129,3 @@ * | ||
* Looks if there is a key in [can-connect/data/inline-cache.INLINE_CACHE] | ||
* that matches the [connection.id] of `params`. If there is, it uses that key's value for the | ||
* that matches the [connect.base.id] of `params`. If there is, it uses that key's value for the | ||
* response data and deletes that key so it can not be reused. | ||
@@ -132,0 +132,0 @@ * |
@@ -0,1 +1,42 @@ | ||
/** | ||
* @module can-connect/data/localstorage-cache data-localstorage-cache | ||
* @parent can-connect.behaviors | ||
* @group can-connect/data/localstorage-cache.identifiers 0 Indentifiers | ||
* @group can-connect/data/localstorage-cache.data-methods 1 Data Methods | ||
* | ||
* Saves raw data in localStorage. | ||
* | ||
* @signature `localStorage( baseConnection )` | ||
* | ||
* Creates a cache of instances and a cache of sets of instances that is | ||
* accessible to read via [can-connect/data/localstorage-cache.getSets], | ||
* [can-connect/data/localstorage-cache.getData], and [can-connect/data/localstorage-cache.getListData]. | ||
* The caches are updated via [can-connect/data/localstorage-cache.createData], | ||
* [can-connect/data/localstorage-cache.updateData], [can-connect/data/localstorage-cache.destroyData], | ||
* and [can-connect/data/localstorage-cache.updateListData]. | ||
* | ||
* [can-connect/data/localstorage-cache.createData], | ||
* [can-connect/data/localstorage-cache.updateData], | ||
* [can-connect/data/localstorage-cache.destroyData] are able to move items in and out | ||
* of sets. | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* `data-localstorage-cache` is often used with a caching strategy like [can-connect/fall-through-cache] or | ||
* [can-connect/cache-requests]. Make sure you configure the connection's [can-connect/data/localstorage-cache.name]. | ||
* | ||
* ``` | ||
* var cacheConnection = connect(["data-localstorage-cache"],{ | ||
* name: "todos" | ||
* }); | ||
* | ||
* var todoConnection = connect(["data-url","fall-through-cache"],{ | ||
* url: "/services/todos", | ||
* cacheConnection: cacheConnection | ||
* }); | ||
* ``` | ||
* | ||
*/ | ||
var getItems = require("can-connect/helpers/get-items"); | ||
@@ -22,9 +63,9 @@ var connect = require("can-connect"); | ||
/** | ||
* @module can-connect/data/localstorage-cache localstorage-cache | ||
* @parent can-connect.behaviors | ||
*/ | ||
module.exports = connect.behavior("data-localstorage-cache",function(baseConnect){ | ||
var behavior = { | ||
// ## Helpers | ||
// an array of each set to the ids it contains | ||
@@ -60,5 +101,3 @@ _sets: null, | ||
}, | ||
getSets: function(){ | ||
return Promise.resolve( this._getSets() ); | ||
}, | ||
getInstance: function(id){ | ||
@@ -91,46 +130,3 @@ //if(!this._instances[id]) { | ||
}, | ||
clear: function(){ | ||
var sets = this.getSetData(); | ||
for(var setKey in sets) { | ||
localStorage.removeItem(this.name+"/set/"+setKey); | ||
} | ||
localStorage.removeItem(this.name+"-sets"); | ||
// remove all instances | ||
var i = 0; | ||
while(i < localStorage.length) { | ||
if(localStorage.key(i).indexOf(this.name+"/instance/") === 0) { | ||
localStorage.removeItem( localStorage.key(i) ); | ||
} else { | ||
i++; | ||
} | ||
} | ||
this._instances = {}; | ||
this._sets = null; | ||
}, | ||
// gets the set from localstorage | ||
getListData: function(set){ | ||
var setKey = sortedSetJSON(set); | ||
var setDatum = this.getSetData()[setKey]; | ||
if(setDatum) { | ||
var localData = localStorage.getItem(this.name+"/set/"+setKey); | ||
if(localData) { | ||
return Promise.resolve( {data: this.getInstances( JSON.parse( localData ) )} ); | ||
} | ||
} | ||
return Promise.reject({message: "no data", error: 404}); | ||
}, | ||
// TODO: Ideally, this should be able to go straight to the instance and not have to do | ||
// much else | ||
getData: function(params){ | ||
var id = this.id(params); | ||
var res = localStorage.getItem(this.name+"/instance/"+id); | ||
if(res){ | ||
return Promise.resolve( JSON.parse(res) ); | ||
} else { | ||
return new Promise.reject({message: "no data", error: 404}); | ||
} | ||
}, | ||
updateSet: function(setDatum, items, newSet) { | ||
@@ -185,23 +181,2 @@ | ||
}, | ||
// creates the set in localstorage | ||
updateListData: function(data, set){ | ||
var items = getItems(data); | ||
var sets = this.getSetData(); | ||
var self = this; | ||
for(var setKey in sets) { | ||
var setDatum = sets[setKey]; | ||
var union = canSet.union(setDatum.set, set, this.algebra); | ||
if(union) { | ||
return this.getListData(setDatum.set).then(function(setData){ | ||
self.updateSet(setDatum, canSet.getUnion(setDatum.set, set, getItems(setData), items, this.algebra), union); | ||
}); | ||
} | ||
} | ||
this.addSet(set, data); | ||
// setData.push({set: set, items: data}); | ||
return Promise.resolve(); | ||
}, | ||
_eachSet: function(cb){ | ||
@@ -230,2 +205,186 @@ var sets = this.getSetData(); | ||
}, | ||
// ## Identifiers | ||
/** | ||
* @property {String} can-connect/data/localstorage-cache.name name | ||
* @parent can-connect/data/localstorage-cache.identifiers | ||
* | ||
* Specify a name to use when saving data in localstorage. | ||
* | ||
* @option {String} This name is used to find and save data in | ||
* localstorage. Instances are saved in `{name}/instance/{id}` | ||
* and sets are saved in `{name}/set/{set}`. | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* ``` | ||
* var cacheConnection = connect(["data-localstorage-cache"],{ | ||
* name: "todos" | ||
* }); | ||
* ``` | ||
*/ | ||
// ## External interface | ||
/** | ||
* @function can-connect/data/localstorage-cache.clear clear | ||
* @parent can-connect/data/localstorage-cache.data-methods | ||
* | ||
* Resets the memory cache so it contains nothing. | ||
* | ||
* @signature `connection.clear()` | ||
* | ||
*/ | ||
clear: function(){ | ||
var sets = this.getSetData(); | ||
for(var setKey in sets) { | ||
localStorage.removeItem(this.name+"/set/"+setKey); | ||
} | ||
localStorage.removeItem(this.name+"-sets"); | ||
// remove all instances | ||
var i = 0; | ||
while(i < localStorage.length) { | ||
if(localStorage.key(i).indexOf(this.name+"/instance/") === 0) { | ||
localStorage.removeItem( localStorage.key(i) ); | ||
} else { | ||
i++; | ||
} | ||
} | ||
this._instances = {}; | ||
this._sets = null; | ||
}, | ||
/** | ||
* @function can-connect/data/localstorage-cache.getSets getSets | ||
* @parent can-connect/data/localstorage-cache.data-methods | ||
* | ||
* Returns the sets contained within the cache. | ||
* | ||
* @signature `connection.getSets(set)` | ||
* | ||
* Returns the sets added by [can-connect/data/localstorage-cache.updateListData]. | ||
* | ||
* @return {Promise<Array<Set>>} A promise that resolves to the list of sets. | ||
* | ||
* @body | ||
* | ||
* ## Use | ||
* | ||
* ``` | ||
* connection.getSets() //-> Promise( [{type: "completed"},{user: 5}] ) | ||
* ``` | ||
* | ||
*/ | ||
getSets: function(){ | ||
return Promise.resolve( this._getSets() ); | ||
}, | ||
/** | ||
* @function can-connect/data/localstorage-cache.getListData getListData | ||
* @parent can-connect/data/localstorage-cache.data-methods | ||
* | ||
* Gets a set of data from localstorage. | ||
* | ||
* @signature `connection.getListData(set)` | ||
* | ||
* Goes through each set add by [can-connect/data/memory-cache.updateListData]. If | ||
* `set` is a subset, uses [connect.base.algebra] to get the data for the requested `set`. | ||
* | ||
* @param {Set} set An object that represents the data to load. | ||
* | ||
* @return {Promise<can-connect.listData>} A promise that resolves if `set` is a subset of | ||
* some data added by [can-connect/data/memory-cache.updateListData]. If it is not, | ||
* the promise is rejected. | ||
*/ | ||
getListData: function(set){ | ||
var setKey = sortedSetJSON(set); | ||
var setDatum = this.getSetData()[setKey]; | ||
if(setDatum) { | ||
var localData = localStorage.getItem(this.name+"/set/"+setKey); | ||
if(localData) { | ||
return Promise.resolve( {data: this.getInstances( JSON.parse( localData ) )} ); | ||
} | ||
} | ||
return Promise.reject({message: "no data", error: 404}); | ||
}, | ||
/** | ||
* @function can-connect/data/localstorage-cache.getData getData | ||
* @parent can-connect/data/localstorage-cache.data-methods | ||
* | ||
* Get an instance's data from localstorage. | ||
* | ||
* @signature `connection.getData(params)` | ||
* | ||
* Looks in localstorage for the requested instance. | ||
* | ||
* @param {Object} params An object that should have the [conenction.id] of the element | ||
* being retrieved. | ||
* | ||
* @return {Promise} A promise that resolves to the item if the memory cache has this item. | ||
* If localstorage does not have this item, it rejects the promise. | ||
*/ | ||
getData: function(params){ | ||
var id = this.id(params); | ||
var res = localStorage.getItem(this.name+"/instance/"+id); | ||
if(res){ | ||
return Promise.resolve( JSON.parse(res) ); | ||
} else { | ||
return new Promise.reject({message: "no data", error: 404}); | ||
} | ||
}, | ||
/** | ||
* @function can-connect/data/localstorage-cache.updateListData updateListData | ||
* @parent can-connect/data/localstorage-cache.data-methods | ||
* | ||
* Saves a set of data in the cache. | ||
* | ||
* @signature `connection.updateListData(listData, set)` | ||
* | ||
* Tries to merge this set of data with any other saved sets of data. If | ||
* unable to merge this data, saves the set by itself. | ||
* | ||
* @param {can-connect.listData} listData | ||
* @param {Set} set | ||
* @return {Promise} Promise resolves if and when the data has been successfully saved. | ||
*/ | ||
updateListData: function(data, set){ | ||
var items = getItems(data); | ||
var sets = this.getSetData(); | ||
var self = this; | ||
for(var setKey in sets) { | ||
var setDatum = sets[setKey]; | ||
var union = canSet.union(setDatum.set, set, this.algebra); | ||
if(union) { | ||
return this.getListData(setDatum.set).then(function(setData){ | ||
self.updateSet(setDatum, canSet.getUnion(setDatum.set, set, getItems(setData), items, this.algebra), union); | ||
}); | ||
} | ||
} | ||
this.addSet(set, data); | ||
// setData.push({set: set, items: data}); | ||
return Promise.resolve(); | ||
}, | ||
/** | ||
* @function can-connect/data/localstorage-cache.createData createData | ||
* @parent can-connect/data/localstorage-cache.data-methods | ||
* | ||
* Called when an instance is created and should be added to cache. | ||
* | ||
* @signature `connection.createData(props)` | ||
* | ||
* Adds `props` to the stored list of instances. Then, goes | ||
* through every set and adds props the sets it belongs to. | ||
*/ | ||
createData: function(props){ | ||
@@ -243,2 +402,14 @@ var self = this; | ||
}, | ||
/** | ||
* @function can-connect/data/localstorage-cache.updateData updateData | ||
* @parent can-connect/data/localstorage-cache.data-methods | ||
* | ||
* Called when an instance is updated. | ||
* | ||
* @signature `connection.updateData(props)` | ||
* | ||
* Overwrites the stored instance with the new props. Then, goes | ||
* through every set and adds or removes the instance if it belongs or not. | ||
*/ | ||
updateData: function(props){ | ||
@@ -277,2 +448,14 @@ var self = this; | ||
}, | ||
/** | ||
* @function can-connect/data/localstorage-cache.destroyData destroyData | ||
* @parent can-connect/data/localstorage-cache.data-methods | ||
* | ||
* Called when an instance should be removed from the cache. | ||
* | ||
* @signature `connection.destroyData(props)` | ||
* | ||
* Goes through each set of data and removes any data that matches | ||
* `props`'s [connect.base.id]. Finally removes this from the instance store. | ||
*/ | ||
destroyData: function(props){ | ||
@@ -279,0 +462,0 @@ var self = this; |
/** | ||
* @module can-connect/data/memory-cache memory-cache | ||
* @module can-connect/data/memory-cache data-memory-cache | ||
* @parent can-connect.behaviors | ||
@@ -26,3 +26,3 @@ * @group can-connect/data/memory-cache.data-methods Data Methods | ||
* | ||
* `memory-cache` is often used with a caching strategy like [can-connect/fall-through-cache] or | ||
* `data-memory-cache` is often used with a caching strategy like [can-connect/fall-through-cache] or | ||
* [can-connect/cache-requests]. | ||
@@ -211,3 +211,3 @@ * | ||
* Goes through each set add by [can-connect/data/memory-cache.updateListData]. If | ||
* `set` is a subset, uses [connection.algebra] to get the data for the requested `set`. | ||
* `set` is a subset, uses [connect.base.algebra] to get the data for the requested `set`. | ||
* | ||
@@ -287,3 +287,3 @@ * @param {Set} set An object that represents the data to load. | ||
* @return {Promise} A promise that resolves to the item if the memory cache has this item. | ||
* If the memory cache, does not have this item it rejects the promise. | ||
* If the memory cache does not have this item, it rejects the promise. | ||
*/ | ||
@@ -380,3 +380,3 @@ getData: function(params){ | ||
* Goes through each set of data and removes any data that matches | ||
* `props`'s [connection.id]. Finally removes this from the instance store. | ||
* `props`'s [connect.base.id]. Finally removes this from the instance store. | ||
*/ | ||
@@ -383,0 +383,0 @@ destroyData: function(props){ |
/** | ||
* @module {connect.Behavior} can-connect/data-url data-url | ||
* @parent can-connect.behaviors | ||
* @group can-connect/data-url.data-methods Data Methods | ||
* @group can-connect/data-url.option Options | ||
* | ||
@@ -126,2 +128,46 @@ * @option {connect.Behavior} | ||
}); | ||
/** | ||
* @property {String|Object} can-connect/data-url.url url | ||
* @parent can-connect/data-url.option | ||
* | ||
* Specify the url and methods that should be used for the "Data Methods". | ||
* | ||
* @option {String} If a string is provided, it's assumed to be a RESTful interface. For example, | ||
* if the following is provided: | ||
* | ||
* ``` | ||
* url: "/services/todos" | ||
* ``` | ||
* | ||
* ... the following methods and requests are used: | ||
* | ||
* - `getListData` - `GET /services/todos` | ||
* - `getData` - `GET /services/todos/{id}` | ||
* - `createData` - `POST /services/todos` | ||
* - `updateData` - `PUT /services/todos/{id}` | ||
* - `destroyData` - `DELETE /services/todos/{id}` | ||
* | ||
* @option {Object} If an object is provided, it can customize each method and URL directly | ||
* like: | ||
* | ||
* ``` | ||
* url: { | ||
* getListData: "GET /services/todos", | ||
* getData: "GET /services/todo/{id}", | ||
* createData: "POST /services/todo", | ||
* updateData: "PUT /services/todo/{id}", | ||
* destroyData: "DELETE /services/todo/{id}" | ||
* } | ||
* ``` | ||
* | ||
* You can provide a `resource` property that works like providing `url` as a string, but overwrite | ||
* other values like: | ||
* | ||
* ``` | ||
* url: { | ||
* resource: "/services/todo", | ||
* getListData: "GET /services/todos" | ||
* } | ||
* ``` | ||
*/ | ||
@@ -133,6 +179,26 @@ // ## pairs | ||
var pairs = { | ||
/** | ||
* @function can-connect/data-url.getListData getListData | ||
* @parent can-connect/data-url.data-methods | ||
*/ | ||
getListData: {prop: "getListData", type: "GET"}, | ||
/** | ||
* @function can-connect/data-url.getData getData | ||
* @parent can-connect/data-url.data-methods | ||
*/ | ||
getData: {prop: "getData", type: "GET"}, | ||
/** | ||
* @function can-connect/data-url.createData createData | ||
* @parent can-connect/data-url.data-methods | ||
*/ | ||
createData: {prop: "createData", type: "POST"}, | ||
/** | ||
* @function can-connect/data-url.updateData updateData | ||
* @parent can-connect/data-url.data-methods | ||
*/ | ||
updateData: {prop: "updateData", type: "PUT"}, | ||
/** | ||
* @function can-connect/data-url.destroyData destroyData | ||
* @parent can-connect/data-url.data-methods | ||
*/ | ||
destroyData: {prop: "destroyData", type: "DELETE"} | ||
@@ -139,0 +205,0 @@ }; |
@@ -12,3 +12,3 @@ /** | ||
* Implements a `getData` and `getListData` that | ||
* check their [connection.cacheConnection] for data and then | ||
* check their [connect.base.cacheConnection] for data and then | ||
* in the background update the instance or list with data | ||
@@ -22,3 +22,3 @@ * retrieved using the base connection. | ||
* To use the `fall-through-cache`, create a connection with a | ||
* [connection.cacheConnection] and a behavior that implements | ||
* [connect.base.cacheConnection] and a behavior that implements | ||
* [connection.getData] and [connection.getListData]. | ||
@@ -56,3 +56,3 @@ * | ||
* | ||
* @demo src/fall-through-cache/fall-through-cache.html | ||
* @demo can-connect/src/fall-through-cache/fall-through-cache.html | ||
* | ||
@@ -129,3 +129,3 @@ * Clicking | ||
* | ||
* Checks the [connection.cacheConnection] for `set`'s data. | ||
* Checks the [connect.base.cacheConnection] for `set`'s data. | ||
* | ||
@@ -199,3 +199,3 @@ * If the cache connection has data, the cached data is returned. Prior to | ||
* | ||
* Then, Looks for registered hydrateInstance callbacks for a given [connection.id] and | ||
* Then, Looks for registered hydrateInstance callbacks for a given [connect.base.id] and | ||
* calls them. | ||
@@ -235,3 +235,3 @@ * | ||
* | ||
* Checks the [connection.cacheConnection] for `params`'s data. | ||
* Checks the [connect.base.cacheConnection] for `params`'s data. | ||
* | ||
@@ -238,0 +238,0 @@ * If the cache connection has data, the cached data is returned. Prior to |
@@ -157,6 +157,5 @@ var helpers = require("can-connect/helpers/"); | ||
// OVERWRITE XHR | ||
var XHR = XMLHttpRequest; | ||
window.XMLHttpRequest = function(){ | ||
global.XMLHttpRequest = function(){ | ||
this._headers = {}; | ||
@@ -163,0 +162,0 @@ }; |
@@ -7,3 +7,3 @@ require("when/es6-shim/Promise"); | ||
// from https://gist.github.com/mythz/1334560 | ||
var win=window, xhrs = [ | ||
var xhrs = [ | ||
function () { return new XMLHttpRequest(); }, | ||
@@ -62,3 +62,3 @@ function () { return new ActiveXObject("Microsoft.XMLHTTP"); }, | ||
case "application/x-javascript": | ||
return win.JSON ? JSON.parse(xhr.responseText) : eval(xhr.responseText); | ||
return JSON.parse(xhr.responseText); | ||
default: | ||
@@ -65,0 +65,0 @@ return xhr.responseText; |
@@ -22,3 +22,3 @@ /** | ||
* [set.subset](https://github.com/canjs/can-set#setsubset) | ||
* of the [connection.listSet]. | ||
* of the [connect.base.listSet]. | ||
* | ||
@@ -70,3 +70,3 @@ * Currently, all items are added at the end of the list | ||
* | ||
* @demo src/real-time/real-time.html | ||
* @demo can-connect/src/real-time/real-time.html | ||
* | ||
@@ -151,3 +151,3 @@ * This example creates a `todoList` function and `todoItem` function | ||
* If there is no instance in the [can.connect/constructor-store.instanceStore] | ||
* for `props`'s [connection.id], an instance is [can-connect/constructor.hydrateInstance hydrated], | ||
* for `props`'s [connect.base.id], an instance is [can-connect/constructor.hydrateInstance hydrated], | ||
* added to the store, and then [can-connect/real-time.createdData] is called with | ||
@@ -154,0 +154,0 @@ * `props` and the hydrated instance's serialized data. [can-connect/real-time.createdData] |
@@ -11,2 +11,3 @@ | ||
* @parent can-connect.behaviors | ||
* @hide | ||
*/ | ||
@@ -13,0 +14,0 @@ module.exports = connect.behavior("service-worker",function(base){ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
1028247
231
17293
587
1
5
2