@bluealba/object-transform
Advanced tools
Comparing version 1.0.0 to 1.0.1
27
index.js
@@ -5,15 +5,20 @@ "use strict"; | ||
const { createSetter } = require("./setter"); | ||
const { ExcludeRule, RemapRule } = require("./rules"); | ||
const { ExcludeTransformation, RenameTransformation, MapTransformation } = require("./rules"); | ||
const TRANSFORMATIONS = { | ||
"rename": RenameTransformation, | ||
"remove": ExcludeTransformation, | ||
"exclude": ExcludeTransformation, | ||
"map": MapTransformation, | ||
} | ||
/** | ||
* Parse rules into an internal format | ||
* Parse transformations into an internal format | ||
*/ | ||
const parseRules = rules => rules.map(definition => { | ||
if (definition.type === "remove" || definition.type === "exclude") { | ||
return new ExcludeRule(definition); | ||
const parseTransformation = txs => txs.map(definition => { | ||
const transformationClass = TRANSFORMATIONS[definition.type]; | ||
if (!transformationClass) { | ||
throw new Error(`Unrecognized transformation type ${definition.type}`); | ||
} | ||
if (definition.type === "rename" || definition.type === "remap") { | ||
return new RemapRule(definition); | ||
} | ||
throw new Error(`Unrecognized rule type ${definition.type}`); | ||
return new transformationClass(definition); | ||
}); | ||
@@ -56,4 +61,4 @@ | ||
module.exports = curry((rules, value) => walk( | ||
parseRules(rules), | ||
module.exports = curry((txs, value) => walk( | ||
parseTransformation(txs), | ||
value, | ||
@@ -60,0 +65,0 @@ Array.isArray(value) ? [] : {}, |
@@ -18,3 +18,3 @@ "use strict"; | ||
it("exclusion rule should prevent simple path from be included in result", () => { | ||
it("exclusion transformation should prevent simple path from be included in result", () => { | ||
const result = transform([ | ||
@@ -34,3 +34,3 @@ { path: "age", type: "remove" } | ||
it("exclusion rule can remove a complex object", () => { | ||
it("exclusion transformation can remove a complex object", () => { | ||
const result = transform([ | ||
@@ -47,3 +47,3 @@ { path: "address", type: "remove" }, | ||
it("exclusion rule can remove a nested property", () => { | ||
it("exclusion transformation can remove a nested property", () => { | ||
const result = transform([ | ||
@@ -63,3 +63,3 @@ { path: "address.city", type: "remove" }, | ||
it("several exclusion rules should work independently", () => { | ||
it("several exclusion transformations should work independently", () => { | ||
const result = transform([ | ||
@@ -76,3 +76,3 @@ { path: "age", type: "remove" }, | ||
it("non-matching exclusion rule should not change anything", () => { | ||
it("non-matching exclusion transformation should not change anything", () => { | ||
const result = transform([ | ||
@@ -97,3 +97,3 @@ { path: "somethingelse", type: "remove" }, | ||
it("rename rule should rename a simple property", () => { | ||
it("rename transformation should rename a simple property", () => { | ||
const result = transform([ | ||
@@ -114,3 +114,3 @@ { path: "lastName", type: "rename", toPath: "surname" } | ||
it("rename rule can rename a complex object", () => { | ||
it("rename transformation can rename a complex object", () => { | ||
const result = transform([ | ||
@@ -131,3 +131,3 @@ { path: "address", type: "rename", toPath: "location" } | ||
it("rename rule can rename a nested property", () => { | ||
it("rename transformation can rename a nested property", () => { | ||
const result = transform([ | ||
@@ -148,3 +148,3 @@ { path: "address.line1", type: "rename", toPath: "address.line" } | ||
it("multiple rename rules are applied in order", () => { | ||
it("multiple rename transformations are applied in order", () => { | ||
const result = transform([ | ||
@@ -166,3 +166,3 @@ { path: "address", type: "rename", toPath: "location" }, | ||
it("rename rule can relocate a property into a newer object", () => { | ||
it("rename transformation can relocate a property into a newer object", () => { | ||
const result = transform([ | ||
@@ -185,3 +185,3 @@ { path: "age", type: "rename", toPath: "data.age" }, | ||
it("rename rule can relocate a property into an existent object", () => { | ||
it("rename transformation can relocate a property into an existent object", () => { | ||
const result = transform([ | ||
@@ -202,3 +202,3 @@ { path: "age", type: "rename", toPath: "address.age" }, | ||
it("rename rule can work with wildcards", () => { | ||
it("rename transformation can work with wildcards", () => { | ||
const result = transform([ | ||
@@ -220,3 +220,39 @@ { path: "*.line1", type: "rename", toPath: "$1.line" }, | ||
it("rules can be used to transform items inside an array", () => { | ||
describe("map", () => { | ||
it("map transformation should map the value", () => { | ||
const result = transform([ | ||
{ path: "firstName", type: "map", fn: value => value + " J." } | ||
])(bart); | ||
expect(result).toEqual({ | ||
firstName: "Bart J.", | ||
lastName: "Simpson", | ||
age: 10, | ||
address: { | ||
line1: "742 Evergreen Terrace", | ||
city: "Springfield" | ||
} | ||
}) | ||
}); | ||
it("map and rename transformation can be used together", () => { | ||
const result = transform([ | ||
{ path: "firstName", type: "rename", toPath: "name" }, | ||
{ path: "name", type: "map", fn: value => value + " J." } | ||
])(bart); | ||
expect(result).toEqual({ | ||
name: "Bart J.", | ||
lastName: "Simpson", | ||
age: 10, | ||
address: { | ||
line1: "742 Evergreen Terrace", | ||
city: "Springfield" | ||
} | ||
}) | ||
}); | ||
}); | ||
it("transformations can be used to transform items inside an array", () => { | ||
const result = transform([ | ||
@@ -223,0 +259,0 @@ { path: "[].address", type: "remove" }, |
{ | ||
"name": "@bluealba/object-transform", | ||
"version": "1.0.0", | ||
"description": "A configurable object tranformer", | ||
"version": "1.0.1", | ||
"description": "A declarative library for transforming objects", | ||
"main": "index.js", | ||
@@ -11,3 +11,3 @@ "author": "Claudio Fernandez <claudio.fernandez@bluealba.com>", | ||
"lint": "eslint '.'", | ||
"coverage": "jest --coverage && cat ./tests/coverage/lcov.info | coveralls" | ||
"coverage": "jest --coverage && cat ./coverage/lcov.info | coveralls" | ||
}, | ||
@@ -23,4 +23,4 @@ "devDependencies": { | ||
"jest": { | ||
"collectCoverage": true | ||
}, | ||
"collectCoverage": true | ||
}, | ||
"repository": { | ||
@@ -27,0 +27,0 @@ "closure": "git", |
# @bluealba/object-transform | ||
[![Build Status](https://travis-ci.org/bluealba/object-transform.svg?branch=master)](https://travis-ci.org/bluealba/object-transform) | ||
[![npm](https://img.shields.io/npm/v/bluealba/object-transform.svg)](https://npmjs.org/package/bluealba/object-transform.svg) | ||
[![npm](https://img.shields.io/npm/dt/bluealba/object-transform.svg)](https://npmjs.org/package/bluealba/object-transform.svg) | ||
[![npm](https://img.shields.io/npm/v/@bluealba/object-transform.svg)](https://npmjs.org/package/@bluealba/object-transform.svg) | ||
[![npm](https://img.shields.io/npm/dt/@bluealba/object-transform.svg)](https://npmjs.org/package/@bluealba/object-transform.svg) | ||
![David](https://img.shields.io/david/bluealba/object-transform.svg) | ||
[![Coverage Status](https://coveralls.io/repos/github/bluealba/object-transform/badge.svg?branch=master)](https://coveralls.io/github/bluealba/object-transform?branch=master) | ||
## Overview | ||
@@ -48,4 +49,4 @@ The main purpose of this module is to perform a series of declarative transformations over an input object returning a modified output object (this library do not have side effect over its input). | ||
const output = transform([ | ||
{ type: "remap", path: "collection", toPath: "books" }, | ||
{ type: "remap", path: "books.[].code", toPath: "books.$1.codes.isbn" }, | ||
{ type: "rename", path: "collection", toPath: "books" }, | ||
{ type: "rename", path: "books.[].code", toPath: "books.$1.codes.isbn" }, | ||
{ type: "exclude", path: "books.[].rating" }, | ||
@@ -94,6 +95,6 @@ ], input); | ||
Even though we plan to expand this currently only two types of transformations are supported | ||
Even though we plan to expand this currently only three basic types of transformations are supported | ||
### Exclude transformation | ||
Formerly known as `remove`. The simplest transformation. It is configured only with a `path`. Any given node whose path matches this rule will be excluded from the final output. Its children won't be included as well, unless a `remap` transformation alters that behavior. | ||
Formerly known as `remove`. The simplest transformation. It is configured only with a `path`. Any given node whose path matches this rule will be excluded from the final output. Its children won't be included as well, unless a `rename` transformation alters that behavior. | ||
@@ -105,4 +106,4 @@ Example: | ||
### Remap transformation | ||
Formerly known as `rename`. The remap takes a `path` and new path (`toPath`). It will simple "move" any node that matches to the newer destination. | ||
### Rename transformation | ||
The rename takes a `path` and new path (`toPath`). It will simple "move" any node that matches to the newer destination. | ||
@@ -117,2 +118,16 @@ Captured group placeholders (`$1`, `$2`...) can be used to match the values of the `path` wildcards (`[]` and `*`) | ||
Example: | ||
``` | ||
{ type: "rename", path: "books.[].author", toPath: "books.$1.authors" } | ||
``` | ||
### Map transformation | ||
The map doesn't affect the matched node path but its value. It's configured with a transformation function `fn` that | ||
receives both the value and the full path and returns the new value. | ||
``` | ||
{ type: "map", path: "books.[].author", fn: (value, path) => value.toUppercase() } | ||
``` | ||
# Known limitations | ||
@@ -119,0 +134,0 @@ Currently we don't support cicles. We are aiming this to perform transformation in JSON-like tree structures. We might |
28
rules.js
@@ -19,3 +19,3 @@ "use strict"; | ||
/** | ||
* A rule determines how a value associated to a certain path of the | ||
* A transformation determines how a value associated to a certain path of the | ||
* `original object` will be treated. It does that by replacing the | ||
@@ -25,6 +25,6 @@ * original setter object that will be used to set the value into | ||
* | ||
* Every rules is associated to a matcher which determines if a given | ||
* path need to be intercepted by this rule or not. | ||
* Every transformation is associated to a matcher which determines if a given | ||
* path need to be intercepted by this transformation or not. | ||
*/ | ||
class Rule { | ||
class Transformation { | ||
constructor({ path }) { | ||
@@ -44,3 +44,3 @@ this.matcher = expressionLens(path); | ||
*/ | ||
class ExcludeRule extends Rule { | ||
class ExcludeTransformation extends Transformation { | ||
apply(setter) { | ||
@@ -51,6 +51,17 @@ return createDummySetter(setter.propertyPath); | ||
class MapTransformation extends Transformation { | ||
constructor({ path, fn }) { | ||
super({ path }); | ||
this.fn = fn; | ||
} | ||
apply(setter) { | ||
return setter.intercept(this.fn) | ||
} | ||
} | ||
/** | ||
* If applies replaces the setter for a dummy setter | ||
*/ | ||
class RemapRule extends Rule { | ||
class RenameTransformation extends Transformation { | ||
constructor({ path, toPath }) { | ||
@@ -70,4 +81,5 @@ super({ path }); | ||
module.exports = { | ||
ExcludeRule, | ||
RemapRule | ||
ExcludeTransformation, | ||
RenameTransformation, | ||
MapTransformation | ||
} |
"use strict"; | ||
const { set, lensPath, mergeRight, over } = require("ramda"); | ||
const { set, lensPath, mergeRight, over, identity } = require("ramda"); | ||
class Setter { | ||
constructor(propertyPath) { | ||
constructor(propertyPath, interceptFn) { | ||
this.propertyPath = propertyPath; | ||
this.interceptFn = interceptFn || identity; | ||
} | ||
set(value, target) { | ||
return set(lensPath(this.propertyPath), value, target); | ||
const interceptedValue = this.interceptFn(value); | ||
return set(lensPath(this.propertyPath), interceptedValue, target); | ||
} | ||
merge(value, target) { | ||
return over(lensPath(this.propertyPath), mergeRight(value), target); | ||
const interceptedValue = this.interceptFn(value); | ||
return over(lensPath(this.propertyPath), mergeRight(interceptedValue), target); | ||
} | ||
@@ -21,2 +24,6 @@ | ||
} | ||
intercept(interceptFn) { | ||
return new this.constructor(this.propertyPath, interceptFn); | ||
} | ||
} | ||
@@ -23,0 +30,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
16817
396
134