Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

slt

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

slt - npm Package Compare versions

Comparing version 0.1.0 to 0.2.1

lib/__tests__/cascadeRules.js

150

lib/slots.js

@@ -9,2 +9,4 @@ "use strict";

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

@@ -14,2 +16,20 @@

var _debug = require("debug");
var _debug2 = _interopRequireDefault(_debug);
var _util = require("util");
var _util2 = _interopRequireDefault(_util);
var d = (0, _debug2["default"])("slt");
var log = (0, _debug2["default"])("slt:log");
function insp(value) {
value = value.toJS ? value.toJS() : value;
value = isArray(value) ? value.join(".") : value;
value = isFunction(value.then) ? "__promise__" : value;
return _util2["default"].inspect(value, { colors: typeof window === "undefined", depth: 0 }).replace("\n", "");
}
function isFunction(v) {

@@ -19,2 +39,10 @@ return Object.prototype.toString.call(v) === "[object Function]";

function isArray(v) {
return Object.prototype.toString.call(v) === "[object Array]";
}
function isString(v) {
return Object.prototype.toString.call(v) === "[object String]";
}
var Slots = (function () {

@@ -55,9 +83,15 @@ function Slots() {

var value = arguments[1] === undefined ? {} : arguments[1];
var state = arguments[2] === undefined ? null : arguments[2];
var _this = this;
var optimistic = arguments[2] === undefined ? true : arguments[2];
var save = arguments[3] === undefined ? true : arguments[3];
var optimistic = arguments[3] === undefined ? true : arguments[3];
var save = arguments[4] === undefined ? true : arguments[4];
path = Slots.path(path);
state = state || this.state;
var reduced = this.reducePathAndValue(path, value);
path = reduced.path;
value = reduced.value;
if (value && isFunction(value.then)) {

@@ -67,2 +101,3 @@ this.promises.push(value);

_this.promises.splice(_this.promises.indexOf(value), 1);
log("RESOLVED %s", insp(path));
_this.set(path, val); // RECURSION with resolved value

@@ -74,55 +109,42 @@ }).error(function (msg) {

})["catch"](function (msg) {})["finally"](function () {});
} else {
var _ret = (function () {
var i = path.length;
var v = value;
while (i--) {
var p = path.slice(0, i);
var tmp = {};
tmp[path.slice(i)] = v;
v = tmp;
if (_this.rules.get(p.join("."))) {
path = p;
value = v;
}
}
var imValue = (0, _immutable.fromJS)(value);
var result = _this.state.mergeDeepIn(path, imValue);
var applyRules = function applyRules() {
var path = arguments[0] === undefined ? new _immutable.List() : arguments[0];
var value = arguments[1] === undefined ? {} : arguments[1];
}
log("SET %s TO %s", insp(path), insp(value));
var imValue = (0, _immutable.fromJS)(value);
var result = imValue.toJS ? state.mergeDeepIn(path, imValue) : state.setIn(path, imValue);
d("Merged \n%s", insp(result));
var applyRules = function applyRules() {
var path = arguments[0] === undefined ? new _immutable.List() : arguments[0];
var value = arguments[1] === undefined ? new _immutable.Map() : arguments[1];
if (!_immutable.Map.isMap(value)) {
return;
}
var rule = _this.rules.get(path.toArray().join("."));
if (isFunction(rule)) {
result = result.merge(rule(result.getIn(path).toJS(), _this.getContext()));
}
value.flip().toList().map(function (k) {
return applyRules(path.push(k), value.get(k));
var rule = _this.rules.get(path.toArray().join("."));
if (isFunction(rule)) {
var p = result.getIn(path);
d("Applying rule on path %s with value %s", insp(path), insp(p));
result = result.mergeDeep(rule(p && p.toJS && p.toJS() || p, _this.getContext(result)).getState());
d("Result is %s", insp(result));
}
if (!_immutable.Map.isMap(value)) {
return;
}
value.flip().toList().map(function (k) {
return applyRules(path.push(k), value.get(k));
});
};
applyRules(new _immutable.List(path), result);
var newState = result;
if (optimistic && !(0, _immutable.is)(this.state, newState)) {
if (save) {
log("SAVE %s", insp(newState));
this.state = newState;
if (!this.promises.length) {
this.onPromisesAreMadeListeners.forEach(function (f) {
return f(_this.state.toJS());
});
};
applyRules(new _immutable.List(path), result);
var newState = result;
if (optimistic && !(0, _immutable.is)(_this.state, newState)) {
if (save) {
_this.state = newState;
if (!_this.promises.length) {
_this.onPromisesAreMadeListeners.forEach(function (f) {
return f(_this.state.toJS());
});
}
_this.onChangeListeners.forEach(function (f) {
return f(_this.state.toJS());
});
}
}
return {
v: result
};
})();
if (typeof _ret === "object") return _ret.v;
this.onChangeListeners.forEach(function (f) {
return f(_this.state.toJS());
});
}
}
return this.getContext(result);
}

@@ -153,3 +175,3 @@ }, {

key: "getContext",
value: function getContext() {
value: function getContext(state) {
var _this2 = this;

@@ -159,3 +181,6 @@

set: function set(path, value) {
return _this2.set(path, value, false, false);
return _this2.set(path, value, state, false, false);
},
getState: function getState() {
return state;
}

@@ -165,2 +190,19 @@ };

}, {
key: "reducePathAndValue",
value: function reducePathAndValue(path, value) {
var i = path.length;
var v = value;
while (i--) {
var p = path.slice(0, i);
var tmp = {};
tmp[path.slice(i)] = v;
v = tmp;
if (this.rules.get(p.join("."))) {
path = p;
value = v;
}
}
return { path: path, value: value };
}
}, {
key: "getRule",

@@ -207,3 +249,3 @@ value: function getRule(path) {

}
return Object.prototype.toString.call(_path) === "[object Array]" && _path || Object.prototype.toString.call(_path) === "[object String]" && _path.split(".") || (function () {
return isArray(_path) && _path || isString(_path) && _path.split(".") || (function () {
throw new Error("path should be an array or dot-separated string or null,\n " + Object.prototype.toString.call(_path) + " given");

@@ -210,0 +252,0 @@ })();

{
"name": "slt",
"version": "0.1.0",
"version": "0.2.1",
"description": "Take care of your state",

@@ -18,2 +18,3 @@ "main": "index.js",

"bluebird": "^2.9.26",
"debug": "^2.2.0",
"immutable": "^3.7.3"

@@ -20,0 +21,0 @@ },

@@ -0,2 +1,116 @@

## DISCLAIMER
I've developed this peace of software during implementation my own project, which was originally started with Flux. I realized that Flux adds a lot of unnecessary (not quite useful) entities and adds a lot of boilerplate to my codebase which is a reason why there is a lot of different implementations. Also I had no time to wait for Facebook Relay (http://facebook.github.io/react/blog/2015/02/20/introducing-relay-and-graphql.html) and I didn't want to bloat my codebase with knowingly out-of-date architecture. Now I glad to introduce my solution to app state managemet. It not dependend/related on any lib except https://facebook.github.io/immutable-js/ which is used for inner state management and not exposed to user (also I have a plan to make pure immutable version w/o any conversion).
Contributers are welcome!
## DESCRIPTION
Slots could be consider as missing part of React. It's like Flux, but better.
Slots could be consider as a missing part of React (not only). It's like Flux, but better.
## TODO
* connections: express middleware, React and others as separate libs
*
* documentation (currently you can learn from tests, it's really self-descriptive)
* add transaction context. Add rollbacks and state history navigation
* add performance tests
* add Promise interface support (e.g. Slots.then ect)
* errors handling
* async tests. More tests for Promises
* rules which are dependent on state conditions (eg Flux waitFor)
* add support for groups of (sub)rules. Could be useful for sharing common rules
* monitoring capabilities (for progress bars, for example) and better debugging
* pure immutable version w/o conversion fromJS and .toJS
* more examples
## USAGE (short man)
###Enable logging
```javascript
window.debug = require("debug");
window.debug.enable("slt:log");
```
###Rules example:
I use https://github.com/AlexeyFrolov/routr-map to parse url.
```javascript
import r from "superagent-bluebird-promise";
import router from "./router";
import Slots from "slt";
const slots = new Slots ({
"request": (req, context) => {
let route = router.match(req.url);
let session = req.session;
route.url = req.url;
return context.set("route", route)
.set("session", req.session);
},
"route": (route, context) => {
let {name, params: { id }} = route;
if (name === "login") {
return context;
}
let url = router.url({name, params: {id}});
return context
.set(url.substr(1).replace("/", "."), r.get("http://example.com/api/" + url)
.then(({body}) => body))
}
});
```
###On the server (express middleware):
```javascript
import slots from "./slots"; // Configured Slots
server.use((req, res, next) => {
slots.onPromisesAreMade((state) => {
state = slots.getStateWithAliases();
var html = render(state);
res.status(state.response && state.response.status || 200).send(html);
slots.reset();
});
let { url, session } = req;
slots.set("request", { url, session });
});
```
###On the client:
```javascript
require("babel/polyfill");
import React from "react";
import Application from "./Application.js";
import slots from "./slots"; // Configured Slots
slots.onChange((state) => {
renderApp(state);
})
function renderApp(state) {
React.render(<Application state={state} />, document.getElementById("root"), () => {
debug("Application has been mounted");
});
}
renderApp(state);
```
```javascript
slots.set("request", {"url": "/users/555e5c37a5311543fc8890c9"})
```
Outputs:
```
slt:log SET 'request' TO { url: '/users/555e5c37a5311543fc8890c9', session: [object Object] } +0ms
slt:log SET 'route' TO { node: [Object], params: [Object], routePath: [Object], query: {}, name: 'users', domain: '', scheme: '',
url: '/users/555e5c37a5311543fc8890c9' } +4ms
slt:log SET 'users.555e5c37a5311543fc8890c9' TO '__promise__' +5ms
slt:log SET 'session' TO [object Object] +5ms
slt:log SAVE { request: [Object], route: [Object], users: [Object], session: [object Object] } +2ms
giftter Detected locale (from browser) is en +20ms
GET /api//users/555e5c37a5311543fc8890c9 200 23.483 ms - -
slt:log RESOLVED 'users.555e5c37a5311543fc8890c9' +31ms
slt:log SET 'users.555e5c37a5311543fc8890c9' TO { _id: '555e5c37a5311543fc8890c9' } +1ms
slt:log SAVE { request: [Object], route: [Object], users: [Object], session: [object Object] } +76ms
```
Final state (w/o 'user', I have different rules, which sets it as well)
![Alt text](https://monosnap.com/file/otw3slLjWwRCYqS12jQM4JXTB4kT2J.png)
import { fromJS, is, Map, List} from "immutable";
import debug from "debug";
import util from 'util';
const d = debug("slt");
const log = debug("slt:log");
function insp(value) {
value = value.toJS ? value.toJS() : value;
value = isArray(value) ? value.join(".") : value;
value = isFunction(value.then) ? "__promise__" : value;
return util.inspect(value, {colors: typeof window === "undefined", depth: 0}).replace('\n', '');
}
function isFunction(v) {

@@ -7,2 +18,10 @@ return Object.prototype.toString.call(v) === "[object Function]";

function isArray(v) {
return Object.prototype.toString.call(v) === "[object Array]";
}
function isString(v) {
return Object.prototype.toString.call(v) === "[object String]";
}
class Slots {

@@ -34,10 +53,16 @@ constructor(rules = {}, state = {}) {

set(path = [], value = {}, optimistic = true, save = true) {
set(path = [], value = {}, state = null, optimistic = true, save = true) {
path = Slots.path(path);
state = state || this.state;
let reduced = this.reducePathAndValue(path, value);
path = reduced.path;
value = reduced.value;
if (value && isFunction(value.then)) {
this.promises.push(value);
value.then((val) => {
this.promises.splice(this.promises.indexOf(value), 1);
this.set(path, val); // RECURSION with resolved value
})
this.promises.splice(this.promises.indexOf(value), 1);
log("RESOLVED %s", insp(path));
this.set(path, val); // RECURSION with resolved value
})
.error((msg) => {

@@ -51,42 +76,35 @@ this.onPromiseErrorListeners.forEach(f => f(msg));

});
} else {
let i = path.length;
let v = value;
while (i--) {
let p = path.slice(0, i);
let tmp = {};
tmp[path.slice(i)] = v;
v = tmp;
if (this.rules.get(p.join("."))) {
path = p;
value = v;
}
}
log("SET %s TO %s", insp(path), insp(value));
let imValue = fromJS(value);
let result = imValue.toJS ? state.mergeDeepIn(path, imValue)
: state.setIn(path, imValue);
d("Merged \n%s", insp(result));
const applyRules = (path = new List(), value = new Map()) => {
let rule = this.rules.get(path.toArray().join("."));
if (isFunction(rule)) {
let p = result.getIn(path);
d("Applying rule on path %s with value %s", insp(path), insp(p));
result = result.mergeDeep(
rule(p && p.toJS && p.toJS() || p, this.getContext(result)).getState());
d("Result is %s", insp(result));
}
let imValue = fromJS(value);
let result = this.state.mergeDeepIn(path, imValue);
const applyRules = (path = new List(), value = {}) => {
if (!Map.isMap(value)) {
return;
if (!Map.isMap(value)) {
return;
}
value.flip().toList().map((k) => applyRules(path.push(k), value.get(k)));
};
applyRules(new List(path), result);
let newState = result;
if (optimistic && !is(this.state, newState)) {
if (save) {
log("SAVE %s", insp(newState));
this.state = newState;
if (!this.promises.length) {
this.onPromisesAreMadeListeners.forEach(f => f(this.state.toJS()));
}
let rule = this.rules.get(path.toArray().join("."));
if (isFunction(rule)) {
result = result.merge(
rule(
result.getIn(path).toJS(), this.getContext()));
}
value.flip().toList().map((k) => applyRules(path.push(k), value.get(k)));
};
applyRules(new List(path), result);
let newState = result;
if (optimistic && !is(this.state, newState)) {
if (save) {
this.state = newState;
if (!this.promises.length) {
this.onPromisesAreMadeListeners.forEach(f => f(this.state.toJS()));
}
this.onChangeListeners.forEach(f => f(this.state.toJS()));
}
this.onChangeListeners.forEach(f => f(this.state.toJS()));
}
return result;
}
return this.getContext(result);
}

@@ -111,6 +129,9 @@

getContext() {
getContext(state) {
return {
set: (path, value) => {
return this.set(path, value, false, false);
return this.set(path, value, state, false, false);
},
getState: () => {
return state;
}

@@ -120,2 +141,18 @@ }

reducePathAndValue(path, value) {
let i = path.length;
let v = value;
while (i--) {
let p = path.slice(0, i);
let tmp = {};
tmp[path.slice(i)] = v;
v = tmp;
if (this.rules.get(p.join("."))) {
path = p;
value = v;
}
}
return { path, value }
}
getRule(path) {

@@ -154,4 +191,3 @@ path = Slots.path(path);

}
return Object.prototype.toString.call(path) === "[object Array]" && path ||
Object.prototype.toString.call(path) === "[object String]" && path.split('.') ||
return isArray(path) && path || isString(path) && path.split('.') ||
(() => { throw new Error (

@@ -158,0 +194,0 @@ `path should be an array or dot-separated string or null,

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc