🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
DemoInstallSign in
Socket

lentil

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

lentil - npm Package Compare versions

Comparing version

to
0.0.2

.jshintrc

8

package.json
{
"name": "lentil",
"version": "0.0.1",
"description": "Utility library for working with Immutable.js and Zelkova",
"version": "0.0.2",
"description": "Partial lenses for TypeScript and JavaScript",
"repository": {

@@ -28,7 +28,3 @@ "type": "git",

"vinyl-map": "^1.0.1"
},
"dependencies": {
"immutable": "^3.4.1",
"zelkova": "^0.0.5"
}
}

@@ -1,92 +0,127 @@

///<reference path="../node_modules/zelkova/dist/zelkova.d.ts"/>
///<reference path="../node_modules/immutable/dist/immutable.d.ts"/>
import Z = require("zelkova");
import I = require("immutable");
module lentil {
"use strict";
export interface Collection<K, V> extends I.Collection<K, V> {
setIn(keyPath: Array<any>, value: V): Collection<K, V>;
updateIn(keyPath: Array<any>, updater: (value: any) => any): Collection<K, V>;
export class Store<A, B> {
set: (value: A) => B;
value: A;
constructor(set: (value: A) => B, value: A) {
this.set = set;
this.value = value;
}
}
export class Selection {
/**
* A partial lens.
*
* `A` is the type of the value and `B` is the type of the "field" the lens is
* referring to - "field" does not necessarily refer to the property of an
* object, it may also be an array index, or infact anything that there
* is a bijection for.
*/
export class PLens<A, B> {
value: any;
_data: Collection<any, any>;
_path: any;
_update: any;
private run: (a: A) => Store<B, A>;
constructor(data: Collection<any, any>, path: Array<any>, update) {
this._data = data;
this._path = path;
this._update = update;
this.value = path.length === 0 ? data : data.getIn(path);
constructor(run: (a: A) => Store<B, A>) {
this.run = run;
}
set(value) {
this._update(this._data.setIn(this._path, value));
compose<C>(that: PLens<C, A>): PLens<C, B> {
return new PLens<C, B>(c => {
var x = that.run(c);
if (x.value == null) return null;
var y = this.run(x.value);
return new Store<B, C>(b => x.set(y.set(b)), y.value);
});
}
modify(fn) {
this._update(this._data.updateIn(this._path, fn));
then<C>(that: PLens<B, C>): PLens<A, C> {
return that.compose(this);
}
select(...args) {
return new Selection(this._data, this._path.concat(args), this._update);
get(a: A, or?: B): B {
var s = this.run(a);
return s ? s.value : or;
}
set(a: A, b: B, or?: A): A {
var s = this.run(a);
return s ? s.set(b) : or;
}
modify(a: A, map: (b: B) => B, or?: A): A {
var s = this.run(a);
return s ? s.set(map(s.value)) : or;
}
}
export class Lentil {
/**
* Create a `PLens` from a getter and setter function. The setter should not
* mutate the original object, but instead return a new value with the
* updated field.
*/
export function plens<A, B>(get: (value: A) => B, set: (value: A, b: B) => A): PLens<A, B> {
return new PLens<A, B>(a => {
return new Store<B, A>(b => set(a, b), get(a));
});
}
signal: Z.Signal<Selection>;
_data: Z.Signal<Collection<any, any>>;
_path: any;
_update: any;
/**
* Interface for things that look and (hopefully) behave like an anonymous
* object.
*/
export interface ObjLike {
[key: string]: any;
}
constructor(data, path, update) {
this._data = data;
this._path = path;
this._update = update;
this.signal = data
.dropRepeats((curr, next) => I.is(curr.getIn(path), next.getIn(path)))
.map(data => new Selection(data, path, update));
/**
* Shallow-copy an object, used when setting with a `prop`-created `PLens`.
*/
function clone(obj: ObjLike): ObjLike {
var result: ObjLike = {};
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
result[k] = obj[k];
}
}
return result;
}
select(...args) {
return new Lentil(this._data, this._path.concat(args), this._update);
}
/**
* Create a `PLens` for a property of an anonymous object.
*/
export function prop<A>(name: string): PLens<ObjLike, A> {
return new PLens<ObjLike, A>(obj => {
return new Store<A, ObjLike>(value => {
var result = clone(obj);
result[name] = value;
return result;
}, obj[name]);
});
}
map(fn) {
var values = [];
var results = [];
return this._data.map(data => {
return data.map((item, i) => {
if (I.is(values[i], data.getIn(this._path.concat([i])))) {
return results[i];
} else {
values[i] = data.getIn(this._path.concat([i]));
return results[i] = fn(new Selection(data, this._path.concat([i]), this._update), i);
}
});
});
}
/**
* Interface for things that look and (hopefully) behave like an array.
*/
export interface ArrayLike {
[key: number]: any;
slice(): ArrayLike;
}
join(input: Z.Signal<Collection<any, any>>): void {
Z.subscribeN(this._data, input, (data, input) => {
if (!I.is(data.getIn(this._path), input)) {
this._update(data.setIn(this._path, input));
}
});
}
/**
* Create a `PLens` for an index of an array.
*/
export function index<A>(i: number): PLens<ArrayLike, A> {
return new PLens<ArrayLike, A>(arr => {
return new Store<A, ArrayLike>(value => {
var result = arr.slice();
result[i] = value;
return result;
}, arr[i]);
});
}
export function create(data, update) {
return new Lentil(data, [], update);
};
}
export = lentil;
"use strict";
var Z = require("zelkova");
var I = require("immutable");
var Lentil = require("../dist/lentil");

@@ -9,73 +7,92 @@

"The signal": {
"should contain the initial data": function (test) {
test.expect(1);
var data = I.fromJS({ a: "foo", b: "bar" });
var src = Z.constant(data);
var l = Lentil.create(src, function () {});
l.signal.subscribe(function (selection) {
test.strictEqual(selection.value, data);
});
test.done();
},
"should not emit equivalent repeat values": function (test) {
test.expect(1);
var data1 = I.fromJS({ a: "foo", b: "bar" });
var data2 = I.fromJS({ a: "foo", b: "bar" });
var chan = Z.channel(data1);
var src = chan.signal;
var l = Lentil.create(src, function () {});
l.signal.subscribe(function (selection) {
test.strictEqual(selection.value, data1);
});
chan.send(data2);
test.done();
}
"plens should create a PLens from a getter and setter": function (test) {
test.expect(1);
var lens = Lentil.plens(
function (x) { return x; },
function (x, y) { return y; }
);
test.ok(lens instanceof Lentil.PLens);
test.done();
},
"selecting": {
"should produce a new Lentil for a more specific part of the data": function (test) {
test.expect(2);
var src = Z.constant(I.fromJS({ a: "foo", b: "bar" }));
var l = Lentil.create(src, function () {});
l.select("a").signal.subscribe(function (selection) { test.equal(selection.value, "foo") });
l.select("b").signal.subscribe(function (selection) { test.equal(selection.value, "bar") });
test.done();
},
"should allow multi-part paths": function (test) {
test.expect(1);
var src = Z.constant(I.fromJS({ a: "foo", b: { test: ["bar"] } }));
var l = Lentil.create(src, function () {});
l.select("b", "test", 0).signal.subscribe(function (selection) {
test.equal(selection.value, "bar");
});
test.done();
},
"should be chainable": function (test) {
test.expect(1);
var src = Z.constant(I.fromJS({ a: "foo", b: { test: ["bar"] } }));
var l = Lentil.create(src, function () {});
l.select("b").select("test").select(0).signal.subscribe(function (selection) {
test.equal(selection.value, "bar");
});
test.done();
}
"plens(x => x, (x, y) => y) should produce an identity lens": function (test) {
test.expect(2);
var lens = Lentil.plens(
function (x) { return x; },
function (x, y) { return y; }
);
test.ok(lens.get(true));
test.ok(lens.set({}, true));
test.done();
},
"joining": {
"should run the update function with new data after patching the original value": function (test) {
test.expect(1);
var data1 = I.fromJS({ a: "foo", b: ["bar"] });
var data2 = I.fromJS({ a: "foo", b: ["bar", "baz", "fizz"] });
var src = Z.constant(data1);
var chan = Z.channel(data1.get("b"));
var l = Lentil.create(src, function (value) {
test.ok(I.is(value, data2))
});
l.select("b").join(chan.signal);
chan.send(I.fromJS(["bar", "baz", "fizz"]));
test.done();
}
"lens.modify should accept the old value and return a new value": function (test) {
test.expect(1);
var lens = Lentil.plens(
function (x) { return x; },
function (x, y) { return y; }
);
var init = {};
test.ok(lens.modify(init, function (x) { return x === init; }));
test.done();
},
"prop should create a PLens for a property of an anonymous object": function (test) {
test.expect(3);
var _x = Lentil.prop("x");
test.ok(_x.get({ x: true }));
var obj1 = { x: false };
var obj2 = _x.set(obj1, true);
test.ok(obj1 !== obj2);
test.ok(obj2.x);
test.done();
},
"index should create a PLens for an index of an array": function (test) {
test.expect(3);
var _1 = Lentil.index(1);
test.ok(_1.get([false, true, false]));
var arr1 = [false, false, false];
var arr2 = _1.set(arr1, true);
test.ok(arr1 !== arr2);
test.ok(arr2[1]);
test.done();
},
"pl1.compose(pl2) should compose pl1 and pl2": function (test) {
test.expect(2);
var _x = Lentil.prop("x");
var _y = Lentil.prop("y");
var _xy = _y.compose(_x);
test.ok(_xy.get({ x: { y: true }}));
test.ok(_xy.set({ x: { y: false }}, true).x.y);
test.done();
},
"pl1.then(pl2) should compose pl2 and pl1": function (test) {
test.expect(2);
var _x = Lentil.prop("x");
var _y = Lentil.prop("y");
var _xy = _x.then(_y);
test.ok(_xy.get({ x: { y: true }}));
test.ok(_xy.set({ x: { y: false }}, true).x.y);
test.done();
},
"set should alter the value of the current lens when the current value is null": function (test) {
test.expect(1);
var _x = Lentil.prop("x");
test.deepEqual(_x.set({ x: null }, true), { x: true });
test.done();
},
"a composed set should not alter the value of the current lens when the 'previous' value is null": function (test) {
test.expect(1);
var _x = Lentil.prop("x");
var _y = Lentil.prop("y");
var _xy = _y.compose(_x);
test.deepEqual(_xy.set({ x: null }, true), undefined);
test.done();
}
};

Sorry, the diff of this file is not supported yet