-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #88 from groveco/release/v1.0.0
- Loading branch information
Showing
17 changed files
with
386 additions
and
708 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"presets": [ | ||
"stage-0", | ||
"env" | ||
], | ||
"plugins": [ | ||
"transform-runtime" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
.DS_Store | ||
.idea | ||
.vscode | ||
npm-debug.log | ||
dist | ||
node_modules | ||
bower_components | ||
coverage | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ Backbone Store is a library for managing and caching data in Backbone applicatio | |
|
||
## Contributing to Backbone Store | ||
|
||
As of `v0.5.1` this package is private to Grove Collaborative. | ||
As of `v1.0.0` this package is private to Grove Collaborative. | ||
It is only published up to Github's packaging service. | ||
To declare a dependency on this package, you need to create a personal access token to fetch/publish packages from a private Github packages. | ||
I'll try to boil it down to a few steps below: | ||
|
@@ -65,19 +65,52 @@ If you're using `@groveco/backbone.store` in your own project: | |
$> popd | ||
~/Projects/backbone.store | ||
``` | ||
5. Run `npm run build` to recompile the library: | ||
```sh | ||
$> pwd | ||
~/Projects/backbone.store | ||
$> npm run build | ||
|
||
> @groveco/[email protected] build ~/Projects/backbone.store | ||
> babel src --out-dir dist | ||
|
||
src/camelcase-dash.js -> dist/camelcase-dash.js | ||
src/collection-proxy.js -> dist/collection-proxy.js | ||
src/http-adapter.js -> dist/http-adapter.js | ||
src/index.js -> dist/index.js | ||
src/internal-model.js -> dist/internal-model.js | ||
src/json-api-parser.js -> dist/json-api-parser.js | ||
src/model-proxy.js -> dist/model-proxy.js | ||
src/repository-collection.js -> dist/repository-collection.js | ||
src/repository.js -> dist/repository.js | ||
src/store.js -> dist/store.js | ||
|
||
$> tree dist/ | ||
dist | ||
├── camelcase-dash.js | ||
├── collection-proxy.js | ||
├── http-adapter.js | ||
├── index.js | ||
├── internal-model.js | ||
├── json-api-parser.js | ||
├── model-proxy.js | ||
├── repository-collection.js | ||
├── repository.js | ||
└── store.js | ||
|
||
0 directories, 10 files | ||
``` | ||
6. Rebuild `other-project` to pick up the changes to `backbone.store` | ||
|
||
5. Rebuild `other-project` to pick up the changes to `backbone.store` | ||
> **Bonus:** Run `npm run build:watch` to rebuild when any file updates. If | ||
> your `other-project` build is _also_ watching for filesystem changes, the | ||
> rebuild in `backbone.store` will trigger it as well. | ||
> **Caveat:** Running `npm install` in `other-project` will destroy the link | ||
> that you made in Step 3 above, so if your build process runs `npm install`, | ||
> you'll have to rerun `npm link` per Step 3 after the build starts... or pass | ||
> `--link` to `npm install`. | ||
> **Caveat:** If running `other-project` inside of a container, using `npm link` | ||
> may prove difficult to link dependencies between local and containered environments. | ||
> One may need to clone this repository within the container and run steps 1-5. | ||
> This is not recommended, but may be inevitable depending on the use case. | ||
## Using Backbone Store | ||
|
||
### Defining models | ||
|
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
/*** | ||
* HttpAdapter | ||
* @module | ||
*/ | ||
import {ajax} from 'jquery' | ||
|
||
/** | ||
* Adapter which works with data over HTTP, based on the options that | ||
* are passed to it's constructor. This class is responsible for | ||
* any CRUD operations that need to be carried out with your {@link https://jsonapi.org/ JSON:API} service | ||
* from within the Backbone.Store. | ||
* @param {Object} options - An object that contains a property, `urlPrefix`, | ||
* that defines the REST service that will be returning a {@link https://jsonapi.org/ JSON:API} response | ||
*/ | ||
class HttpAdapter { | ||
constructor (options = {}) { | ||
this.urlPrefix = options.urlPrefix | ||
|
||
this.serializeRequests = false | ||
this._outstandingRequests = new Set() | ||
} | ||
|
||
buildUrl (type, id) { | ||
let idPath = `/${id}` | ||
let typePath = `/${type}` | ||
let path = this.urlPrefix || '' | ||
|
||
path += typePath | ||
|
||
if (id != null) { | ||
path += idPath | ||
} | ||
|
||
return path + '/' | ||
} | ||
|
||
/** | ||
* Get entity by link. | ||
* @private | ||
* @param {string} link - Link to entity. | ||
* @returns {Promise} Promise for fetched data. | ||
*/ | ||
get (link, query) { | ||
return this._ajax('GET', link, query) | ||
.then(body => JSON.parse(body)) | ||
} | ||
|
||
/** | ||
* Create entity. | ||
* @private | ||
* @param {string} link - Entity url. | ||
* @param {object} attributes - Data to create entity with. | ||
* @returns {Promise} Promise for created data. | ||
*/ | ||
create (link, payload) { | ||
return this._ajax('POST', link, payload) | ||
.then(body => body && JSON.parse(body)) | ||
} | ||
|
||
/** | ||
* Update entity. | ||
* @private | ||
* @param {string} link - Entity url. | ||
* @param {object} attributes - Data to update entity with. | ||
* @returns {Promise} Promise for updated data. | ||
*/ | ||
update (link, payload) { | ||
return this._ajax('PATCH', link, payload) | ||
.then(body => JSON.parse(body)) | ||
} | ||
|
||
/** | ||
* Destroy entity. | ||
* @private | ||
* @param {string} link - Entity self link. | ||
* @returns {Promise} Promise for destroy. | ||
*/ | ||
destroy (link) { | ||
return this._ajax('DELETE', link) | ||
} | ||
|
||
_ajax (type, url, data) { | ||
let headers = { | ||
'Accept': 'application/vnd.api+json', | ||
'Content-Type': 'application/vnd.api+json' | ||
} | ||
|
||
// Stringify data before any async stuff, just in case it's accidentally a mutable object (e.g. | ||
// some instrumented Vue data) | ||
if (data && ['PATCH', 'POST'].indexOf(type) > -1) { | ||
data = JSON.stringify(data) | ||
} | ||
|
||
let promise | ||
if (this.serializeRequests) { | ||
// Wait for all requests to settle (either with success or rejection) before making request | ||
const promises = Array.from(this._outstandingRequests) | ||
.map(promise => promise.catch(() => {})) | ||
|
||
promise = Promise.all(promises) | ||
.then(() => this._makeRequest({ url, type, headers, data })) | ||
} else { | ||
promise = this._makeRequest({ url, type, headers, data }) | ||
} | ||
|
||
this._outstandingRequests.add(promise) | ||
const removeFromOutstandingRequests = () => { | ||
this._outstandingRequests.delete(promise) | ||
} | ||
promise.then(removeFromOutstandingRequests, removeFromOutstandingRequests) | ||
|
||
return promise | ||
} | ||
|
||
_makeRequest ({ url, type, headers, data }) { | ||
return new Promise((resolve, reject) => { | ||
let request = { | ||
url, | ||
type, | ||
headers, | ||
success: (data, textStatus, jqXhr) => { | ||
if (!data && jqXhr.status !== 204) { | ||
throw new Error(`request returned ${jqXhr.status} status without data`) | ||
} | ||
return resolve(data) | ||
}, | ||
error: (response) => { | ||
if (response.readyState === 0 || response.status === 0) { | ||
// this is a canceled request, so we literally should do nothing | ||
return | ||
} | ||
|
||
const error = new Error(`request for resource, ${url}, returned ${response.status} ${response.statusText}`) | ||
error.response = response | ||
reject(error) | ||
}, | ||
// being explicit about data type so jQuery doesn't "intelligent guess" wrong | ||
// changing this may not break tests, but does behave badly in prod | ||
dataType: 'text', | ||
data | ||
} | ||
|
||
ajax(request) | ||
}) | ||
} | ||
} | ||
|
||
export default HttpAdapter |
Oops, something went wrong.