Skip to content

Commit

Permalink
feat(Registry): UT and Fix Type projection error. Add an interface to…
Browse files Browse the repository at this point in the history
… the curried function
  • Loading branch information
emyann committed Oct 21, 2016
1 parent 62aa941 commit 4a3097d
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 71 deletions.
119 changes: 88 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Morphism
# Morphism

[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] [![Coverage percentage][coveralls-image]][coveralls-url]
> Helps you to transform any object structure to another
Expand All @@ -7,20 +8,39 @@
- Twitter: [@renaudin_yann][twitter-account]
- [GitHub Issues](https://github.com/emyann/morphism/issues)

## Installation
## Getting started

Install `morphism` using npm.

```sh
$ npm install --save morphism
npm install --save morphism
```

Then require it into any module.

```js
const Morphism = require('morphism');
```

Or using ES6 import style

```js
import Morphism from 'morphism';
```

If you're using [browserify](http://browserify.org/), the `morphism` npm module
also works from the browser.


## What does it do?

Morphism uses a semantic configuration to go through the collection of graph objects you have to process. Then it extracts and computes the value from the specified path(s). Finally, it sets this value to the destination property from the schema.

### Usage
## Usage
Morphism is curried function that allows a partial application with a semantic configuration. You can use it in 2 ways:

#### As a Mapper factory
### As a Mapper factory
```js
const Morphism = require('morphism'); // (ES6 Import) import Morphism from 'morphism';

let mapping = { ... }
let collectionOfObjects = [ ... ]
Expand All @@ -32,7 +52,7 @@ myAwesomeMapper(collectionOfObjects);
myAwesomeMapper(anotherCollection);
```

#### As a Static instance
### As a Static instance
```js
const Morphism = require('morphism');

Expand Down Expand Up @@ -69,6 +89,7 @@ let data = [{


### Simple mapping

```js
let data = [ ... ];
let mapping = {
Expand All @@ -79,14 +100,16 @@ let mapping = {

let results = Morphism(mapping, data);

console.log(results[0]) ==>
{
pseudo: 'John',
lastName: 'Smith',
city: 'New York'
}
// results[0]: {
// pseudo: 'John',
// lastName: 'Smith',
// city: 'New York'
// }
```


### Value transformation

```js
let data = [ ... ];
let mapping = {
Expand All @@ -103,40 +126,42 @@ let mapping = {
let mapper = Morphism(mapping);
let results = mapper(data);

console.log(results[0]) ==>
{
pseudo: 'John',
lastName: 'Smith',
city: 'new york',// <== toLowerCase
nbContacts: 2 // <== computed from the object
}
// results[0]): {
// pseudo: 'John',
// lastName: 'Smith',
// city: 'new york',// <== toLowerCase
// nbContacts: 2 // <== computed from the object
// }
```


### Values Aggregation

```js
let data = [ ... ];

let mapping = {
let mapping = {
user: ['firstName','lastName'] // aggregate the values to an object
city: 'address.city'
};

let results = Morphism(mapping, data);

console.log(results[0]) ==>
{
user: {
'firstName': 'John',
'lastName': 'Smith'
},
city: 'New York'
}
// results[0]: {
// user: {
// 'firstName': 'John',
// 'lastName': 'Smith'
// },
// city: 'New York'
// }
```

### Registry of Mappers
### Mappers Registry

Morphism provides a local registry in which you can store your mappers' configuration.
Morphism provides a powerful local registry where you can store your mappings' configuration by specifying a Type.
The transformation sequences are stored as a function in a WeakMap to speed the processing.

**Register a mapping configuration**
```js
class User {
constructor(firstName, lastName, phoneNumber){
Expand All @@ -155,6 +180,38 @@ Morphism.map(User, data)
mapUser(data);
```

### API

#### Register

Register a mapper for a specific type. The schema is optional.

```js
Morphism.register(type, schema:{});
```

#### Map

Map a collection of objects to the specified type

```js
Morphism.map(type, data:[]);
```

#### Get or Set an existing mapper configuration

```js
Morphism.setMapper(type, schema:{});
```

#### Delete a registered mapper

```js
Morphism.deleteMapper(type, schema:{});
```



## License

MIT © [Yann Renaudin][twitter-account]
Expand Down
66 changes: 46 additions & 20 deletions lib/morphism.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as _ from 'lodash';

export default function Morphism(schema, items) {
export default function Morphism(schema, items, type) {
let _schema = _.clone(schema);
let transformer = (items) => {
return _.chain(items)
Expand All @@ -22,34 +22,34 @@ export default function Morphism(schema, items) {
return predicate.fn(delta);
}
});


return transformedObject;



}).value();
});
};

if (items === undefined) {
if (items === undefined && type === undefined) {
return (items) => {
return transformer(items);
return transformer(items).value();
};
} else if (type) {
return (items) => {
return transformer(items).map((o) => { return _.assignIn(new type(), o); }).value(); // TODO: Refactor this in a chain of responsibility pattern
};
} else {
return transformer(items);
}

else {
return transformer(items).value();
}
}


function factory(type, schema, items) {
let typeFields = _.keys(new type());
let defaultSchema = _.zipObject(typeFields,typeFields);
let finalSchema = _.assign(defaultSchema,schema);
let defaultSchema = _.zipObject(typeFields, typeFields);
let finalSchema = _.assign(defaultSchema, schema);

return Morphism(finalSchema, items);
return Morphism(finalSchema, items, type);
}


_.memoize.Cache = WeakMap;
const _registry = _.memoize(factory);

Expand All @@ -62,28 +62,54 @@ class _Morphism {
*/
static register(type, schema) {
if (!type && !schema) {
throw new Error('When using register, you must provide at least a [Type] or a mapping [configuration]');
throw new Error('type paramater is required when register a mapping');
} else if (_Morphism.exists(type)) {
throw new Error(`A mapper for ${type.name} has already been registered`);
}
return _registry(type, schema); // Store the result of the executed function in a WeakMap cache object
}

static map(type, data) {
let results = _registry(type)(data);
let assign = function (type) { return _.assignIn(new type, this); };
return _.invokeMap(results, assign, type);
return _registry(type)(data);
}

static getMapper(type) {
return _registry.cache.get(type);
}

static get mappers() {
return _registry.cache;
}

static exists(type) {
return _registry.cache.has(type);
}

static setMapper(type, schema) {
if (!schema) {
throw new Error(`The schema must be an Object. Found ${schema}`);
} else if (!_Morphism.exists(type)) {
throw new Error(`The type ${type.name} is not registered. Register it using \`Mophism.register(${type.name}, schema)\``);
} else {
let fn = factory(type, schema);
_registry.cache.set(type, fn);
return _registry(type);
}

}

static deleteMapper(type) {
return _registry.cache.delete(type);
}
}

let api = {
register: _Morphism.register,
map: _Morphism.map,
getMapper: _Morphism.getMapper
getMapper: _Morphism.getMapper,
setMapper: _Morphism.setMapper,
deleteMapper: _Morphism.deleteMapper,
mappers: _Morphism.mappers
};

_.assignIn(Morphism, api);
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"eslint-config-xo-space": "^0.14.0",
"eslint-plugin-babel": "^3.3.0",
"expect": "^1.20.2",
"faker": "^3.1.0",
"gulp": "^3.9.0",
"gulp-babel": "^6.1.2",
"gulp-coveralls": "^0.1.0",
Expand Down

0 comments on commit 4a3097d

Please sign in to comment.