# Object diff
XXX Intro
- [Object diff](#object-diff)
	- [Motivation](#motivation)
		- [Goals / Features](#goals--features)
	- [General](#general)
	- [Installation and loading](#installation-and-loading)
		- [Diff](#diff)
			- [Diff class API](#diff-class-api)
			- [Diff object API](#diff-object-api)
			- [Supported JavaScript objects](#supported-javascript-objects)
			- [Extended 'Text' object support](#extended-text-object-support)
			- [Options](#options)
		- [Deep compare](#deep-compare)
		- [Patterns](#patterns)
		- [Patterns (EXPERIMENTAL)](#patterns-experimental)
		- [JSON compatibility](#json-compatibility)
	- [Extending Diff](#extending-diff)
	- [The Diff format](#the-diff-format)
		- [Flat](#flat)
		- [Tree](#tree)
	- [Contacts, feedback and contributions](#contacts-feedback-and-contributions)
	- [License](#license)
## Motivation
XXX
### Goals / Features
- Full JSON *diff* support
- Support for JavaScript objects without restrictions
- *Text diff* support
- ~~Optional attribute order support~~ (not done yet)
- Configurable and extensible implementation
- As simple as possible
XXX alternatives
## General
```javascript
var {Diff, cmp} = require('object-diff')
var Bill = {
	name: 'Bill',
	age: 20,
	hair: 'black',
	skills: [
	],
}
var Ted = {
	name: 'Ted',
	age: 20,
	hair: 'blond',
	skills: [
	],
}
var diff = Diff(Bill, Ted)
// XXX examine the diff...
var PERSON = {
	name: STRING,
	age: NUMBER,
	hair: STRING,
	skills: ARRAY(STRING),
}
// we can "type-check" things...
cmp(Bill, PERSON)
```
## Installation and loading
Install the package:
```shell
$ npm install --save object-diff
```
Load the module:
```javascript
var diff = require('object-diff')
```
This module supports both [requirejs](https://requirejs.org/) and [node's](https://nodejs.org/) `require(..)`.
XXX list basic use-cases:
- creating / manipulating a diff
- patching objects
- deep comparisons and patterns
### Diff
Create a diff object:
```javascript
var diff = new Diff(A, B)
```
#### Diff class API
`Diff.cmp(A, B) -> bool`  
Deep compare `A` to `B`.
`Diff.clone(
)`  
Clone the `Diff` constructor, useful for extending or tweaking the type handlers (see: [Extending](#extending-diff) below).
`Diff.fromJSON(json) -> diff`  
Build a diff object from JSON (exported via `.json()`).
#### Diff object API
`diff.patch(X) -> X'`  
Apply "diff* (or *patch*) `X` to `X'` state.
`diff.unpatch(X') -> X`  
Undo *diff" application to `X'` returning it to `X` state.
`diff.check(X) -> bool`  
Check if *diff* is compatible/applicable to `X`. This essentially checks if the *left hand side* of the *diff* matches the corresponding nodes of `X`.
`diff.json() -> JSON`  
Serialize the *diff* to JSON. Note that the output may or may not be JSON compatible depending on the inputs.
#### Supported JavaScript objects
The object support can be split into two, basic objects that are stored as-is and containers that support item changes when their types match.
All JavaScript objects/values are supported in the basic form / as-is.
Containers that support item changes include:
- `Object`
- `Array`
- ~~`Map` / `WeakMap`~~ *(planned but not done yet)*
- ~~`Set` / `WeakSet`~~ *(planned but not done yet)*
Additionally attribute changes are supported for all non basic objects (i.e. anything for which `typeof X` yeilds `"object"`). This can be disabled by setting `options.no_attributes` to `true` (see: [Options](#options) below).
#### Extended 'Text' object support
A *text* is JavaScript string that is long (>1000 chars, configurable in [Oprions](#options)) and multiline string.
A *text* is treated as an `Array` containing the string split into lines (split by `'\n'`).
Shorter strings or strings containing just a single line are treated as *basic* monolithic string objects and included in the *diff* as-is.
#### Options
```javascript
{
	// if true return a tree diff format...
	tree_diff: false | true,
	// if true, NONE change items will not be removed from the diff...
	keep_none: false | true,
	// Minimum length of a string for it to be treated as Text...
	//
	// If this is set to a negative number Text diffing is disabled.
	//
	// NOTE: a string must also contain at least one \n to be text 
	//		diffed...
	min_text_length: 100 | -1,
	// If true, disable attribute diff for non Object's...
	//
	// XXX should be more granular...
	no_attributes: false | true,
	// Plaeholders to be used in the diff..
	//
	// Set these if the default values conflict with your data...
	//
	// XXX remove these from options in favor of auto conflict 
	//		detection and hashing...
	NONE: null | { .. },
	EMPTY: null | { .. },
}
```
### Deep compare
```javascript
cmp(A, B)
Diff.cmp(A, B)
```
XXX
### Patterns
XXX General description...
`ANY`  
Matches anything
`NOT(A)`  
Match anything but `A`
`OR(A[, .. ])`  
Match if *one* of the arguments matches
`AND(A[, .. ])`  
Matches of *all* of the arguments match
XXX examples...
### Patterns (EXPERIMENTAL)
`NUMBER(min, max)`  
`IN(A)`  
`AT(A, K)`  
`OF(A, N)`  
### JSON compatibility
```javascript
var json = diff.json()
var diff2 = Diff.fromJSON(json)
```
Note that the output of `.json()` may not be JSON compatible if the input objects are not json compatible. The reasoning here is simple: `object-diff` is a *diffing* module and not a *serializer*.
The simple way to put this is: if the inputs are JSON-compatible the output of `.json()` is going to also be JSON-compatible.
The big picture is a bit more complicated, `Diff(..)` and friends support allot more than simply JSON, this would include any types, attributes on all objects and loops/recursive structures.
`.fromJSON(..)` does not care about JSON compatibility and will be happy with any output of `.json()`.
## Extending Diff
Create a new diff constructor:
```javascript
var ExtendedDiff = diff.Diff.clone()
```
This has the same API as `Diff` and inherits from it, but it has an independent handler map that can be manipulated without affecting the original `Diff` setup.
When building a *diff* type checking is done in two stages:
1. via the `.check(..)` method of each implementing handler, this approach is used for *synthetic* type handlers, as an exmple look at `'Text'` that matches long multi-line string objects.
2. type-checking via `instanceof` / `.construtor`, this is used for JavaScript objects like `Array` and `Object` instances, for example.
Hence we have two types of handler objects, those that implement `.check(..)` and can have any arbitrary object as a key (though a nice and readable string is recommended), and objects that have the constructor as key against which `instanceof` checks are done.
`.check(..)` has priority to enable handlers to intercept handling of special cases, `'Text'` handler would be a good example.
If types of the not equal object pair mismatch `'Basic'` is used and both are stored in the *diff* as-is.
`.priority` enables sorting of checks and handlers within a stage, can be set to a positive, negative `Number` or `null`, priorities with same numbers are sorted in order of occurrence.
Adding new *synthetic* type handler:
```javascript
ExtendedDiff.types.set('SomeType', {
	// Type check priority (optional)...
	//
	// Types are checked in order of occurrence in .handlers unless
	// type .priority is set to a non 0 value.
	//
	// Default priorities:
	//	Text:		100
	//		Needs to run checks before 'Basic' as its targets are
	//		long strings that 'Basic' also catches.
	//	Basic:		50
	//		Needs to be run before we do other object checks.
	//	Object:		-100
	//		Needs to run after everything else as it will match any
	//		set of objects.
	//
	// General guide:
	//	>50			- to be checked before 'Basic'
	//	<50 and >0	- after Basic but before unprioritized types
	//	<=50 and <0	- after unprioritized types but before Object
	//	<=100		- to be checked after Object -- this is a bit 
	//				  pointless in JavaScript.
	//
	// NOTE: when this is set to 0, then type will be checked in 
	//		order of occurrence...
	priority: null,
	// If set to true will disable additional attribute diffing on 
	// matching objects...
	no_attributes: false | true,
	// Check if obj is compatible (optional)...
	//
	// 	.check(obj[, options])
	//		-> bool
	//
	compatible: function(obj, options){
		// ...
	},
	// Handle/populate the diff of A and B...
	//
	// Input diff format:
	//	{
	//		type: ,
	//	}
	//
	handle: function(obj, diff, A, B, options){
		// ...
	},
	// Walk the diff...
	//
	// This will pass each change to func(..) and return its result...
	//
	//	.walk(diff, func, path)
	//		-> res
	//
	// NOTE: by default this will not handle attributes (.attrs), so
	//		if one needs to handle them Object's .walk(..) should be 
	//		explicitly called...
	//		Example:
	//			walk: function(diff, func, path){
	//				var res = []
	//
	//				// handle specific diff stuff...
	//				
	//				return res
	//					// pass the .items handling to Object
	//					.concat(this.typeCall(Object, 'walk', diff, func, path))
	//			}
	walk: function(diff, func, path){
		// ...
	},
 	// Patch the object...
	//
	patch: function(target, key, change, root, options){
		// ...
	},
	// Finalize the patch process (optional)...
	//
	// This is useful to cleanup and do any final modifications.
	//
	// This is expected to return the result.
	//
	// see: 'Text' for an example.
	postPatch: function(result){
		..
		return result
	},
 	// Reverse the change in diff...
	//
	reverse: function(change){
		// ...
	},
})
```
Adding new type handler (checked via `instanceof` / `.constructor`):
```javascript
ExtendedDiff.types.set(SomeOtherType, {
	priority: null,
	// NOTE: .check(..) is not needed here...
	// the rest of the methods are the same as before...
	// ...
})
```
Remove an existing type handler:
```javascript
ExtendedDiff.types.delete('Text')
```
The [source code](./diff.js#L1098) is a good example for this as the base `Diff(..)` is built using this API, but note that we are registering types on the `Types` object rather than on the `Diff` itself, there is no functional difference other than how the main code is structured internally.
The handler methods are all called in the context of the `Diff.types` instance, this instance is created per `Diff`'s method call and is destroyed right after the method is done, thus it is save to use the context for caching.
To call a different type handler's methods use:
```javascript
this.typeCall(HandlerType, 'method', ...args)
```
For an example see: `Object` handler's `.walk(..)` in [diff.js](./diff.js#L1178).
## The Diff format
### Flat
This is the main and default format used to store diff information.
```javascript
[]
```
### Tree
This format is used internally but may be useful for introspection.
```javascript
```
## Contacts, feedback and contributions
- https://github.com/flynx/object-diff.js
- XXX npm
- https://github.com/flynx
## License
[BSD 3-Clause License](./LICENSE)
Copyright (c) 2018, Alex A. Naanou,
All rights reserved.