Skip to content

Commit d2499a5

Browse files
initial commit
0 parents  commit d2499a5

File tree

13 files changed

+709
-0
lines changed

13 files changed

+709
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
coverage

.jshintrc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"node": true,
3+
"browser": true,
4+
"esnext": false,
5+
"bitwise": false,
6+
"camelcase": false,
7+
"curly": false,
8+
"eqeqeq": true,
9+
"immed": true,
10+
"latedef": true,
11+
"mocha": true,
12+
"newcap": true,
13+
"noarg": true,
14+
"quotmark": "single",
15+
"regexp": true,
16+
"undef": true,
17+
"unused": "vars",
18+
"strict": true,
19+
"trailing": true,
20+
"smarttabs": false,
21+
"globals": {},
22+
"predef": [],
23+
"indent": 2,
24+
"devel": true,
25+
"noempty": true
26+
}

.npmignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
test
3+
coverage
4+
.gitignore
5+
Makefile
6+
.travis.yml

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
language: "node_js"
2+
node_js:
3+
- "0.10"
4+
- "0.12"
5+
- "iojs"
6+
script: make test-coveralls

Makefile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
REPORTER = spec
2+
test:
3+
@NODE_ENV=test ./node_modules/.bin/mocha -b --reporter $(REPORTER) --recursive test
4+
5+
lint:
6+
./node_modules/.bin/jshint ./lib ./index.js
7+
8+
test-cov:
9+
./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- -R spec --recursive test
10+
11+
test-coveralls:
12+
./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- -R spec --recursive test && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
13+
rm -rf lib-cov
14+
15+
clean:
16+
rm -rf ./coverage
17+
18+
.PHONY: test

README.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
JSON Merge Patch
2+
===============
3+
4+
[![build status][travis-image]][travis-url]
5+
[![Test coverage][coveralls-image]][coveralls-url]
6+
7+
An implementation of the JSON Merge Patch [RFC 7396](http://tools.ietf.org/html/rfc7396)
8+
9+
JSON Merge Patch [(RFC 7396)](http://tools.ietf.org/html/rfc7396) is a standard format that
10+
allows you to update a JSON document by sending the changes rather than the whole document.
11+
JSON Merge Patch plays well with the HTTP PATCH verb (method) and REST style programming.
12+
13+
14+
## Install
15+
16+
Install the current version (and save it as a dependency):
17+
18+
### npm
19+
20+
```sh
21+
$ npm install json-merge-patch --save
22+
```
23+
24+
25+
## Usage
26+
27+
Applying patches:
28+
```js
29+
var source = {
30+
"title": "Goodbye!",
31+
"author" : {
32+
"givenName" : "John",
33+
"familyName" : "Doe"
34+
}
35+
};
36+
37+
var patch = {
38+
"title": 'Hello!',
39+
"author": {
40+
"familyName": null
41+
}
42+
}
43+
44+
var target = jsonmergepatch.apply(source, patch);
45+
46+
// target = {
47+
// "title": "Hello!",
48+
// "author" : {
49+
// "givenName" : "John",
50+
// }
51+
// }
52+
```
53+
54+
Generating patches:
55+
```js
56+
var source = {
57+
"title": "Goodbye!",
58+
"author" : "John Doe"
59+
};
60+
61+
var target = {
62+
"title": "Hello!",
63+
};
64+
65+
var patch = jsonmergepatch.generate(source, target);
66+
67+
// patch = {
68+
// "title": 'Hello!',
69+
// "author": null
70+
// }
71+
```
72+
73+
## API
74+
75+
#### jsonmergepatch.apply (`obj` Object, `patch` Object) : Object
76+
77+
Applies `patch` on `obj`.
78+
79+
#### jsonmergepatch.generate (`source` Object, `target` Object) : `patch` Object
80+
81+
Generates a `patch` Object from source and target Object.
82+
83+
## Running tests
84+
85+
```sh
86+
make test
87+
```
88+
89+
# License
90+
91+
MIT
92+
93+
[travis-image]: https://img.shields.io/travis/pierreinglebert/json-merge-patch/master.svg?style=flat-square
94+
[travis-url]: https://travis-ci.org/pierreinglebert/json-merge-patch
95+
[coveralls-image]: https://img.shields.io/coveralls/pierreinglebert/json-merge-patch/master.svg?style=flat-square
96+
[coveralls-url]: https://coveralls.io/r/pierreinglebert/json-merge-patch?branch=master

index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
'use strict';
2+
3+
module.exports.apply = require('./lib/apply');
4+
module.exports.generate = require('./lib/generate');

lib/apply.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
module.exports = function apply(target, patch) {
4+
if(patch === null || typeof patch !== 'object' || Array.isArray(patch)) {
5+
return patch;
6+
}
7+
if(target === null || typeof target !== 'object' || Array.isArray(target)) {
8+
target = {};
9+
}
10+
var keys = Object.keys(patch);
11+
for(var i=0; i<keys.length; i++) {
12+
var key = keys[i];
13+
if(patch[key] === null) {
14+
if(target.hasOwnProperty(key)) {
15+
delete target[key];
16+
}
17+
} else {
18+
target[key] = apply(target[key], patch[key]);
19+
}
20+
}
21+
return target;
22+
};

lib/generate.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
'use strict';
2+
3+
function arrayEquals(before, after) {
4+
if(before.length !== after.length) {
5+
return false;
6+
}
7+
for(var i=0; i<before.length; i++) {
8+
if(after[i] !== before[i]) {
9+
return false;
10+
}
11+
}
12+
return true;
13+
}
14+
15+
module.exports = function generate(before, after) {
16+
if(before === null || after === null ||
17+
typeof before !== 'object' || typeof after !== 'object' ||
18+
Array.isArray(before) !== Array.isArray(after)) {
19+
return after;
20+
}
21+
22+
if(Array.isArray(before)) {
23+
if(!arrayEquals(before, after)) {
24+
return after;
25+
}
26+
return undefined;
27+
}
28+
29+
var patch = {};
30+
var beforeKeys = Object.keys(before);
31+
var afterKeys = Object.keys(after);
32+
33+
// removed elements
34+
beforeKeys.filter(function(key) {
35+
return afterKeys.indexOf(key) === -1;
36+
})
37+
.forEach(function(key) {
38+
patch[key] = null;
39+
});
40+
41+
// new elements
42+
afterKeys.filter(function(key) {
43+
return beforeKeys.indexOf(key) === -1;
44+
})
45+
.forEach(function(key) {
46+
patch[key] = after[key];
47+
});
48+
49+
// modified elements
50+
beforeKeys.filter(function(i) {
51+
return afterKeys.indexOf(i) !== -1;
52+
})
53+
.forEach(function(key) {
54+
if(before[key] !== null && typeof before[key] === 'object') {
55+
var subPatch = generate(before[key], after[key]);
56+
if(subPatch !== undefined) {
57+
patch[key] = subPatch;
58+
}
59+
} else if(before[key] !== after[key]) {
60+
patch[key] = after[key];
61+
}
62+
});
63+
return (Object.keys(patch).length > 0 ? patch : undefined);
64+
};

package.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "json-merge-patch",
3+
"version": "0.1.0",
4+
"description": "Implementation of JSON Merge Patch (RFC 7396)",
5+
"main": "index.js",
6+
"directories": {
7+
"test": "test"
8+
},
9+
"dependencies": {
10+
"chai": "^2.2.0"
11+
},
12+
"devDependencies": {
13+
"istanbul": "^0.3.11",
14+
"mocha": "^2.2.1"
15+
},
16+
"scripts": {
17+
"test": "make test"
18+
},
19+
"repository": {
20+
"type": "git",
21+
"url": "https://github.com/pierreinglebert/json-merge-patch.git"
22+
},
23+
"keywords": [
24+
"JSON",
25+
"Merge",
26+
"Patch",
27+
"rfc",
28+
"7396"
29+
],
30+
"author": "Pierre Inglebert <pierre.inglebert@gmail.com>",
31+
"license": "MIT",
32+
"bugs": {
33+
"url": "https://github.com/pierreinglebert/json-merge-patch/issues"
34+
},
35+
"homepage": "https://github.com/pierreinglebert/json-merge-patch"
36+
}

0 commit comments

Comments
 (0)