Skip to content

Commit

Permalink
fix(loki): fix error if passed parameters are not serializable for tr…
Browse files Browse the repository at this point in the history
…ansform (#43)

Using new 'CloneMethod.SHALLOW_RECURSE_OBJECTS' for transform parameter substitution to avoid error when base transform step(s) have non serializable properties. (from techfort/LokiJS#578)
  • Loading branch information
Viatorus authored Nov 22, 2017
1 parent 3509429 commit b4e5b45
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 8 deletions.
77 changes: 77 additions & 0 deletions packages/loki/spec/generic/transforms.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,83 @@ describe("transforms", () => {
});
});

describe("parameterized transform with non-serializable non-params", function () {
it("works", function () {

interface Person {
name: string;
age: number;
}


const db = new Loki("tx.db");
const items = db.addCollection<Person>("items");

items.insert({name: "mjolnir", age: 5});
items.insert({name: "tyrfing", age: 9});

let mapper = function (item: Person) {
return item.age;
};

let averageReduceFunction = function (values: number[]) {
let sum = 0;

values.forEach(function (i) {
sum += i;
});

return sum / values.length;
};

// so ideally, transform params are useful for
// - extracting values that will change across multiple executions, and also
// - extracting values which are not serializable so that the transform can be
// named and serialized along with the database.
//
// The transform used here is not serializable so this test is just to verify
// that our parameter substitution method does not have problem with
// non-serializable transforms.

let tx1 = [
{
type: "mapReduce",
mapFunction: mapper,
reduceFunction: averageReduceFunction
}
];

let tx2 = [
{
type: "find",
value: {
age: {
"$gt": "[%lktxp]minimumAge"
},
}
},
{
type: "mapReduce",
mapFunction: mapper,
reduceFunction: averageReduceFunction
}
] as any;

// no data() call needed to mapReduce
expect(items.chain(tx1) as any as number).toBe(7);
expect(items.chain(tx1, {foo: 5}) as any as number).toBe(7);
// params will cause a recursive shallow clone of objects before substitution
expect(items.chain(tx2, {minimumAge: 4}) as any as number).toBe(7);

// make sure original transform is unchanged
expect(tx2[0].type).toEqual("find");
expect(tx2[0].value.age.$gt).toEqual("[%lktxp]minimumAge");
expect(tx2[1].type).toEqual("mapReduce");
expect(typeof tx2[1].mapFunction).toEqual("function");
expect(typeof tx2[1].reduceFunction).toEqual("function");
});
});

describe("parameterized where", () => {
it("works", () => {

Expand Down
18 changes: 15 additions & 3 deletions packages/loki/src/clone.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
/* global jQuery */
export type ANY = any;

export function clone<T>(data: T, method: CloneMethod = CloneMethod.PARSE_STRINGIFY) : T {
export function clone<T>(data: T, method: CloneMethod = CloneMethod.PARSE_STRINGIFY): T {
if (data === null || data === undefined) {
return null;
}

let cloned: object;
let cloned: any;

switch (method) {
case CloneMethod.PARSE_STRINGIFY:
Expand All @@ -28,6 +27,18 @@ export function clone<T>(data: T, method: CloneMethod = CloneMethod.PARSE_STRING
cloned = Object.create(data.constructor.prototype);
Object.assign(cloned, data);
break;
case CloneMethod.SHALLOW_RECURSE_OBJECTS:
// shallow clone top level properties
cloned = clone(data, CloneMethod.SHALLOW);
const keys = Object.keys(data);
// for each of the top level properties which are object literals, recursively shallow copy
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (typeof data[key] === "object" && data[key].constructor.name === "Object") {
cloned[key] = clone(data[key], CloneMethod.SHALLOW_RECURSE_OBJECTS);
}
}
break;
default:
break;
}
Expand Down Expand Up @@ -57,4 +68,5 @@ export enum CloneMethod {
JQUERY_EXTEND_DEEP,
SHALLOW,
SHALLOW_ASSIGN,
SHALLOW_RECURSE_OBJECTS,
}
9 changes: 4 additions & 5 deletions packages/loki/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/**
* Created by toni on 1/27/17.
*/
import {clone, CloneMethod} from "./clone";

export type ANY = any;

export function copyProperties(src: object, dest: object) {
Expand Down Expand Up @@ -41,8 +40,8 @@ export function resolveTransformParams(transform: ANY, params: object) {

// iterate all steps in the transform array
for (idx = 0; idx < transform.length; idx++) {
// clone transform so our scan and replace can operate directly on cloned transform
clonedStep = JSON.parse(JSON.stringify(transform[idx]));
// clone transform so our scan/replace can operate directly on cloned transform
clonedStep = clone(transform[idx], CloneMethod.SHALLOW_RECURSE_OBJECTS);
resolvedTransform.push(resolveTransformObject(clonedStep, params));
}

Expand Down

0 comments on commit b4e5b45

Please sign in to comment.