Skip to content

Commit

Permalink
Merge pull request #88 from groveco/release/v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
stephen-bunn authored Nov 16, 2021
2 parents c60347b + f38d84b commit 6913710
Show file tree
Hide file tree
Showing 17 changed files with 386 additions and 708 deletions.
9 changes: 9 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"presets": [
"stage-0",
"env"
],
"plugins": [
"transform-runtime"
]
}
2 changes: 1 addition & 1 deletion .gitignore
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
Expand Down
47 changes: 40 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
11 changes: 0 additions & 11 deletions babel.config.js

This file was deleted.

13 changes: 0 additions & 13 deletions jest.config.js

This file was deleted.

34 changes: 21 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
{
"name": "@groveco/backbone.store",
"main": "src/index.js",
"main": "dist/index.js",
"module": "src/index.js",
"repository": "git://github.com/groveco/backbone.store.git",
"version": "0.5.1",
"version": "1.0.0",
"scripts": {
"prepack": "NODE_ENV=development npm install",
"prepack": "NODE_ENV=development npm install && npm run build",
"build": "babel src --out-dir dist",
"build:watch": "babel --watch src --out-dir dist",
"build:docs": "jsdoc --readme README.md src/* -d ./docs",
"test": "jest",
"test": "jest ./tests",
"lint": "eslint ./src/index.js"
},
"publishConfig": {
Expand All @@ -16,23 +18,29 @@
"dependencies": {
"backbone": "^1.3.3",
"clone": "^2.1.0",
"jquery": "^2.2.4",
"jquery": "^3.6.0",
"underscore": "^1.9.1"
},
"devDependencies": {
"@babel/cli": "^7.10.1",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-jest": "^24.9.0",
"core-js": "^3.6.5",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.1",
"babel-preset-stage-0": "^6.24.1",
"eslint": "^3.19.0",
"eslint-config-standard": "^7.0.1",
"eslint-plugin-jest": "^19.0.1",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^3.0.1",
"jest": "^24.9.0",
"jest-junit": "^10.0.0",
"jest": "^22.4.0",
"jest-junit": "^1.3.0",
"jsdoc": "^3.6.2",
"sinon": "^9.0.3"
"sinon": "^1.17.2"
},
"jest": {
"collectCoverage": true,
"coverageDirectory": "./coverage",
"testResultsProcessor": "jest-junit",
"testURL": "http://localhost/"
}
}
148 changes: 148 additions & 0 deletions src/http-adapter.js
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
Loading

0 comments on commit 6913710

Please sign in to comment.