├── lib
└── versions.txt
├── .travis.yml
├── docs
└── 20140703-freaklies
│ ├── freaklies.pdf
│ ├── Makefile
│ ├── figures
│ ├── Makefile
│ ├── mpxerr.tex
│ ├── data-events.mp
│ ├── macros.mp
│ ├── data-events.svg
│ └── data-events.mps
│ └── freaklies.tex
├── examples
├── autocomplete
│ ├── autocomplete.css
│ ├── autocomplete-rxjs.html
│ ├── autocomplete.html
│ ├── autocomplete-rxjs.js
│ └── autocomplete.js
├── suggestions
│ ├── Makefile
│ ├── style.css
│ ├── index.html
│ ├── README.md
│ └── suggestions.js
└── counter
│ ├── Makefile
│ ├── style.css
│ ├── bacon.html
│ ├── rxjs.html
│ ├── index.html
│ ├── bacon-bus.html
│ ├── rxjs-behavior.html
│ ├── counter-bacon-bus.js
│ ├── counter-rxjs.js
│ ├── counter-rxjs-behavior.js
│ ├── counter.js
│ ├── README.md
│ ├── counter-bacon.js
│ └── bacon.md
├── .npmignore
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── benchmark
├── Makefile
├── README.md
└── diamond.js
├── test
├── egal-test.js
├── corner-test.js
├── option-test.js
├── menrva-test.js
├── signal-test.js
├── convenience-test.js
├── lens-test.js
└── transaction-test.js
├── .jshintrc
├── .jshintrc.examples
├── src
├── egal.js
├── util.js
├── lens.js
├── option.js
├── menrva.js
├── convenience.js
├── signal.js
└── transaction.js
├── scripts
└── version.js
├── LICENSE
├── package.json
├── Makefile
├── README.md
└── dist
├── menrva.min.js
├── menrva.min.js.map
└── menrva.standalone.js
/lib/versions.txt:
--------------------------------------------------------------------------------
1 | jquery.js 2.1.1
2 | Bacon.js 0.7.16
3 | rx.lite.js 2.2.25
4 | lodash.js 2.4.1
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 | - "0.11"
5 | script: make test coveralls
6 |
--------------------------------------------------------------------------------
/docs/20140703-freaklies/freaklies.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phadej/menrva/HEAD/docs/20140703-freaklies/freaklies.pdf
--------------------------------------------------------------------------------
/examples/autocomplete/autocomplete.css:
--------------------------------------------------------------------------------
1 | * {
2 | font-family: Helvetica, Arial, Sans-Serif;
3 | font-size: 13px;
4 | }
5 |
--------------------------------------------------------------------------------
/docs/20140703-freaklies/Makefile:
--------------------------------------------------------------------------------
1 | all : freaklies.pdf
2 |
3 | freaklies.pdf : freaklies.tex
4 | make -C figures
5 | pdflatex freaklies.tex
6 |
--------------------------------------------------------------------------------
/examples/suggestions/Makefile:
--------------------------------------------------------------------------------
1 | LJS=../../node_modules/.bin/ljs
2 |
3 | README.md : suggestions.js
4 | $(LJS) -o README.md suggestions.js
5 |
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .jshintrc
2 | .travis.yml
3 | .npmignore
4 |
5 | Makefile
6 |
7 | dist/
8 | coverage/
9 | benchmark/
10 | test/
11 | examples/
12 | docs/
13 | scripts/
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 |
4 | # tex auxillary files
5 | *.aux
6 | *.log
7 | *.out
8 | *.toc
9 |
10 | # metapos
11 | *.mpx
12 |
13 | # benchmark results
14 | benchmark/*.txt
15 |
--------------------------------------------------------------------------------
/examples/counter/Makefile:
--------------------------------------------------------------------------------
1 | LJS=../../node_modules/.bin/ljs
2 |
3 | all : README.md bacon.md
4 |
5 | README.md : counter.js
6 | $(LJS) -o README.md counter.js
7 |
8 | bacon.md : counter-bacon.js
9 | $(LJS) -o bacon.md counter-bacon.js
10 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | - **0.0.7** `sequence` and `record`
2 | - **0.0.6** Convenience methods
3 | - **0.0.5** Lens
4 | - **0.0.4** Internal improvements
5 | - **0.0.3** Slight changes in API
6 | - **0.0.2** Basic data-flow functionality
7 | - **0.0.1** Name reservation
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | In lieu of a formal styleguide, take care to maintain the existing coding style.
2 |
3 | Add tests for any new or changed functionality. 100% coverage isn't hard. Semantic coverage is important too though.
4 |
5 | Note: `README.md` is autogenerated file.
6 |
--------------------------------------------------------------------------------
/benchmark/Makefile:
--------------------------------------------------------------------------------
1 | BENCHMARKS=diamond
2 |
3 | .PHONY : all quick full clean
4 |
5 | all : quick
6 |
7 | quick : $(BENCHMARKS:%=%.quick.txt)
8 |
9 | full : $(BENCHMARKS:%=%.txt)
10 |
11 | clean :
12 | rm -f $(BENCHMARKS:%=%.txt) $(BENCHMARKS:%=%.quick.txt)
13 |
14 | %.txt : %.js
15 | node $< | tee $@
16 |
17 | %.quick.txt : %.js
18 | node $< --quick | tee $@
19 |
--------------------------------------------------------------------------------
/test/egal-test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var menrva = require("../src/menrva.js");
4 | var assert = require("assert");
5 |
6 | describe("egal", function () {
7 | it("consider nan's as equal", function () {
8 | assert(menrva.egal(NaN, NaN));
9 | });
10 |
11 | it("consider +0 and -0 non-equal", function () {
12 | assert(!menrva.egal(+0, -0));
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "quotmark": "double",
3 | "camelcase": true,
4 | "shadow": "outer",
5 | "trailing": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "latedef": "nofunc",
10 | "newcap": true,
11 | "noarg": true,
12 | "sub": true,
13 | "undef": true,
14 | "unused": "vars",
15 | "boss": true,
16 | "eqnull": true,
17 | "node": true
18 | }
19 |
--------------------------------------------------------------------------------
/docs/20140703-freaklies/figures/Makefile:
--------------------------------------------------------------------------------
1 | FIGURES:=data-events
2 |
3 | all : $(FIGURES:%=%.mps) $(FIGURES:%=%.svg)
4 |
5 | MPOST=mpost
6 | SVGPARAMS=-s outputformat='"svg"' -s outputtemplate='"%j.svg"'
7 | MPSPARAMS=-s outputformat='"mps"' -s outputtemplate='"%j.mps"'
8 |
9 | %.mps : %.mp macros.mp
10 | $(MPOST) $(MPSPARAMS) $<
11 |
12 | %.svg : %.mp macros.mp
13 | $(MPOST) $(SVGPARAMS) $<
14 |
--------------------------------------------------------------------------------
/.jshintrc.examples:
--------------------------------------------------------------------------------
1 | {
2 | "quotmark": "double",
3 | "camelcase": true,
4 | "shadow": "outer",
5 | "trailing": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "latedef": "nofunc",
10 | "newcap": true,
11 | "noarg": true,
12 | "sub": true,
13 | "undef": true,
14 | "unused": "vars",
15 | "boss": true,
16 | "eqnull": true,
17 | "browser": true,
18 | "globals": {
19 | "$": true,
20 | "menrva": true,
21 | "Bacon": true,
22 | "Rx": true,
23 | "_": true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/docs/20140703-freaklies/figures/mpxerr.tex:
--------------------------------------------------------------------------------
1 | %&latex
2 |
3 | \gdef\mpxshipout{\shipout\hbox\bgroup
4 | \setbox0=\hbox\bgroup}
5 | \gdef\stopmpxshipout{\egroup \dimen0=\ht0 \advance\dimen0\dp0
6 | \dimen1=\ht0 \dimen2=\dp0
7 | \setbox0=\hbox\bgroup
8 | \box0
9 | \ifnum\dimen0>0 \vrule width1sp height\dimen1 depth\dimen2
10 | \else \vrule width1sp height1sp depth0sp\relax
11 | \fi\egroup
12 | \ht0=0pt \dp0=0pt \box0 \egroup}
13 | \mpxshipout% line 19 ./mvc.mp
14 | \textbf{MODEL}%
15 | \stopmpxshipout
16 | \end{document}
17 |
--------------------------------------------------------------------------------
/docs/20140703-freaklies/figures/data-events.mp:
--------------------------------------------------------------------------------
1 | input macros;
2 |
3 | verbatimtex
4 | %&latex
5 | \documentclass{article}
6 | \begin{document}
7 | etex
8 |
9 | dim = 1.5cm;
10 |
11 | beginfig(1);
12 |
13 | path p;
14 | p = (-dim,0)..(0,-dim)..(dim,0);
15 | fill p{up}..(0,0){-1,-2}..{up}cycle;
16 | draw p..(0,dim)..cycle;
17 |
18 | draw thelabel(btex \textbf{data} etex, (-.5dim, 0)) withcolor .0white;
19 | draw thelabel(btex \textbf{events} etex, (.5dim, 0)) withcolor white;
20 |
21 | endfig;
22 |
23 | verbatimtex
24 | \end{document}
25 | etex
26 | end
27 |
--------------------------------------------------------------------------------
/src/egal.js:
--------------------------------------------------------------------------------
1 | /*
2 | * menrva
3 | * https://github.com/phadej/menrva
4 | *
5 | * Copyright (c) 2014 Oleg Grenrus
6 | * Licensed under the MIT license.
7 | */
8 |
9 | "use strict";
10 |
11 | /**
12 | ### Equalities
13 |
14 | #### egal
15 |
16 | > egal (a, b) : boolean
17 |
18 | Identity check. `Object.is`. http://wiki.ecmascript.org/doku.php?id=harmony:egal
19 | */
20 | function egal(a, b) {
21 | if (a === 0 && b === 0) {
22 | return 1/a === 1/b;
23 | } else if (a !== a) {
24 | return b !== b;
25 | } else {
26 | return a === b;
27 | }
28 | }
29 |
30 | module.exports = egal;
31 |
--------------------------------------------------------------------------------
/examples/counter/style.css:
--------------------------------------------------------------------------------
1 | .container {
2 | width: 200px;
3 | box-sizing: border-box;
4 | padding: 5px;
5 | border: 1px solid #eee;
6 | text-align: center;
7 | }
8 | .container div {
9 | padding: 5px;
10 | }
11 | #counter {
12 | border-bottom: 1px solid #ccc;
13 | font-weight: bold;
14 | font-family: sans-serif;
15 | font-size: 20px;
16 | }
17 | button {
18 | background-color: #369;
19 | color: white;
20 | border: none;
21 | border-radius: 5px;
22 | font-size: 20px;
23 | }
24 | button:active {
25 | background-color: #69c;
26 | }
27 | button:disabled {
28 | background-color: #999;
29 | }
30 |
--------------------------------------------------------------------------------
/examples/suggestions/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: sans-serif;
3 | padding: 10px;
4 | }
5 | h2 {
6 | font-weight: bold;
7 | display: inline-block;
8 | }
9 | .refresh {
10 | font-size: 80%;
11 | margin-left: 10px;
12 | }
13 | .header {
14 | background: #ECECEC;
15 | padding: 5px;
16 | }
17 | .suggestions {
18 | border: 2px solid #ECECEC;
19 | }
20 | li {
21 | padding: 5px;
22 | }
23 | li img {
24 | width: 40px;
25 | height: 40px;
26 | border-radius: 20px;
27 | }
28 | li .username, li .close {
29 | display: inline-block;
30 | position: relative;
31 | bottom: 15px;
32 | left: 5px;
33 | }
34 |
--------------------------------------------------------------------------------
/examples/counter/bacon.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | –
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/examples/counter/rxjs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | –
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/examples/autocomplete/autocomplete-rxjs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Autocomplete Example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Wikipedia search:
13 |
14 | Start typing:
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/autocomplete/autocomplete.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Autocomplete Example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Wikipedia search:
13 |
14 | Start typing:
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/counter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | –
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/examples/counter/bacon-bus.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | –
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/examples/counter/rxjs-behavior.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | –
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/benchmark/README.md:
--------------------------------------------------------------------------------
1 | # Benchmark results
2 |
3 | ## 2014-07-07
4 |
5 | ```
6 | Bacon.js x 170 ops/sec ±2.06% (343 runs sampled)
7 | Bacon.js Bus→Property x 169 ops/sec ±2.39% (359 runs sampled)
8 | Bacon.js Model x 153 ops/sec ±2.04% (339 runs sampled)
9 | Rx.JS Cold x 1,100 ops/sec ±2.70% (337 runs sampled)
10 | menrva x 1,994 ops/sec ±1.46% (379 runs sampled)
11 | ```
12 |
13 | ## 2014-07-26
14 |
15 | ```
16 | Bacon.js x 103 ops/sec ±2.22% (342 runs sampled)
17 | Bacon.js Bus→Property x 107 ops/sec ±1.29% (344 runs sampled)
18 | Bacon.js Model x 160 ops/sec ±1.61% (347 runs sampled)
19 | Rx.JS Cold x 1,103 ops/sec ±2.02% (338 runs sampled)
20 | menrva x 1,867 ops/sec ±1.11% (365 runs sampled)
21 | ```
22 |
--------------------------------------------------------------------------------
/examples/counter/counter-bacon-bus.js:
--------------------------------------------------------------------------------
1 | /**
2 | Inspired by [simple counter](http://baconjs.github.io/)
3 | */
4 | $(function () {
5 | "use strict";
6 |
7 | var MIN_VALUE = 0;
8 |
9 | var $valueBus = new Bacon.Bus();
10 | var $value = $valueBus.toProperty(0);
11 |
12 | $value.assign($("#counter"), "text");
13 |
14 | $value.onValue(function (x) {
15 | var down = $("#down");
16 | if (x <= MIN_VALUE) {
17 | down.attr("disabled", "disabled");
18 | } else {
19 | down.removeAttr("disabled");
20 | }
21 | });
22 |
23 | var $up = $("#up").asEventStream("click");
24 | var $down = $("#down").asEventStream("click");
25 |
26 | var $events = $up.map(1).merge($down.map(-1));
27 |
28 | var $valueState = $events.scan(0, function(x,y) {
29 | return Math.max(MIN_VALUE, x + y);
30 | });
31 |
32 | $valueBus.plug($valueState);
33 | });
34 |
--------------------------------------------------------------------------------
/examples/counter/counter-rxjs.js:
--------------------------------------------------------------------------------
1 | /**
2 | Inspired by [simple counter](http://baconjs.github.io/)
3 | */
4 | $(function () {
5 | "use strict";
6 |
7 | var MIN_VALUE = 0;
8 |
9 | var upButton = $("#up");
10 | var downButton = $("#down");
11 | var counterEl = $("#counter");
12 |
13 | var $up = Rx.Observable.fromEvent(upButton[0], "click");
14 | var $down = Rx.Observable.fromEvent(downButton[0], "click");
15 |
16 | var $counter =
17 | // map up to 1, down to -1
18 | $up.map(function () { return 1; }).merge($down.map(function () { return -1; }))
19 | .scan(0, function(x,y) { return Math.max(MIN_VALUE, x + y); })
20 | .startWith(0);
21 |
22 | $counter.subscribe(function (x) {
23 | counterEl.text(x);
24 |
25 | if (x <= MIN_VALUE) {
26 | downButton.attr("disabled", "disabled");
27 | } else {
28 | downButton.removeAttr("disabled");
29 | }
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/test/corner-test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var menrva = require("../src/menrva.js");
4 | var assert = require("assert");
5 | var _ = require("lodash");
6 | var chai = require("chai");
7 |
8 | describe("corner cases", function () {
9 | it("combine with itself", function () {
10 | var a = menrva.source(1);
11 | var combineCount = 0;
12 | var b = menrva.combine(a, a, a, function (x, y, z) {
13 | combineCount += 1;
14 | return x + y + z;
15 | });
16 | var onValueCount = 0;
17 | b.onValue(function () {
18 | onValueCount += 1;
19 | })
20 |
21 | chai.expect(b.value()).to.equal(3);
22 | chai.expect(combineCount).to.equal(1);
23 | chai.expect(onValueCount).to.equal(1);
24 |
25 | var tx = menrva.transaction();
26 | a.set(tx, 2);
27 | tx.commit();
28 |
29 | chai.expect(b.value()).to.equal(6);
30 | chai.expect(combineCount).to.equal(2);
31 | chai.expect(onValueCount).to.equal(2);
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/scripts/version.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var fs = require("fs");
4 | var jsstana = require("jsstana");
5 | var esprima = require("esprima");
6 | var estraverse = require("estraverse");
7 |
8 | var pkg = JSON.parse(fs.readFileSync(__dirname + "/../package.json").toString());
9 | var version = pkg.version;
10 |
11 | console.log("package.json version:", version);
12 |
13 | var filename = __dirname + "/../src/menrva.js";
14 | var contents = fs.readFileSync(filename).toString();
15 | var syntax = esprima.parse(contents, { loc: true, range: true });
16 |
17 | var versionLocation;
18 |
19 | estraverse.traverse(syntax, {
20 | enter: function (node) {
21 | var m = jsstana.match("(var version (?value string))", node);
22 | if (m) {
23 | versionLocation = m.value.range;
24 | }
25 | },
26 | });
27 |
28 | if (versionLocation) {
29 | contents = contents.substr(0, versionLocation[0]) + "\"" + version + "\"" + contents.substr(versionLocation[1]);
30 |
31 | fs.writeFileSync(filename, contents);
32 | }
33 |
--------------------------------------------------------------------------------
/examples/counter/counter-rxjs-behavior.js:
--------------------------------------------------------------------------------
1 | /**
2 | Inspired by [simple counter](http://baconjs.github.io/)
3 | */
4 | $(function () {
5 | "use strict";
6 |
7 | var MIN_VALUE = 0;
8 |
9 | var upButton = $("#up");
10 | var downButton = $("#down");
11 | var counterEl = $("#counter");
12 |
13 | var $value = new Rx.BehaviorSubject(0);
14 |
15 | $value.subscribe(function (x) {
16 | counterEl.text(x);
17 |
18 | if (x <= MIN_VALUE) {
19 | downButton.attr("disabled", "disabled");
20 | } else {
21 | downButton.removeAttr("disabled");
22 | }
23 | });
24 |
25 | var $up = Rx.Observable.fromEvent(upButton[0], "click");
26 | var $down = Rx.Observable.fromEvent(downButton[0], "click");
27 |
28 | var $state =
29 | // map up to 1, down to -1
30 | $up.map(function () { return 1; }).merge($down.map(function () { return -1; }))
31 | .scan(0, function(x,y) { return Math.max(MIN_VALUE, x + y); })
32 | .startWith(0);
33 |
34 | $state.subscribe(function (value) {
35 | $value.onNext(value);
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/docs/20140703-freaklies/figures/macros.mp:
--------------------------------------------------------------------------------
1 | prologues := 3;
2 |
3 | dimX=2.5cm;
4 | dimY=-1.5cm;
5 |
6 | dimC=2pt;
7 |
8 | vardef clippathp(expr p, a, b) =
9 | save s, t, r;
10 |
11 | numeric s;
12 | numeric t;
13 | path r;
14 |
15 | (whatever, t1) = a intersectiontimes p;
16 | (whatever, t2) = b intersectiontimes (reverse p);
17 |
18 | subpath (t1, length(p) - t2) of p
19 | enddef;
20 |
21 | vardef clippath(expr p, a, b) =
22 | clippathp(p, bbox(a), bbox(b))
23 | enddef;
24 |
25 | def midpoint(expr p) = point 0.5 of p enddef;
26 |
27 | vardef largerbbox(expr p) =
28 | save q, z;
29 | path q;
30 | pair z[];
31 |
32 | q = bbox p;
33 |
34 | z1 = ulcorner q shifted (-3mm, 1mm);
35 | z2 = urcorner q shifted (3mm, 1mm);
36 | z3 = lrcorner q shifted (3mm, -1mm);
37 | z4 = llcorner q shifted (-3mm, -1mm);
38 |
39 | z1--z2--z3--z4--cycle
40 | enddef;
41 |
42 | color greenColor, blueColor, redColor, orangeColor, greyColor;
43 |
44 | greenColor := (0, 0.5, 0);
45 | blueColor := (0, 0, 0.5);
46 | redColor := (0.5, 0, 0);
47 | orangeColor := (1, 0.5, 0);
48 | greyColor := (0.5, 0.5, 0.5);
49 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Oleg Grenrus
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/examples/counter/counter.js:
--------------------------------------------------------------------------------
1 | /**
2 | Inspired by [simple counter](http://baconjs.github.io/)
3 |
4 | Implemented in *menrva*, [Bacon.js](https://github.com/baconjs/bacon.js) and [RxJS](https://github.com/Reactive-Extensions/RxJS).
5 |
6 | Bacon.js and RxJS variants, come in two flavours, *direct* and *data-flowish*.
7 | */
8 | $(function () {
9 | "use strict";
10 |
11 | var MIN_VALUE = 0;
12 |
13 | var $value = menrva.source(0);
14 |
15 | $value.onValue(function (x) {
16 | $("#counter").text(x);
17 | });
18 |
19 | $value.onValue(function (x) {
20 | var down = $("#down");
21 | if (x <= MIN_VALUE) {
22 | down.attr("disabled", "disabled");
23 | } else {
24 | down.removeAttr("disabled");
25 | }
26 | });
27 |
28 | function inc(x) {
29 | return x + 1;
30 | }
31 |
32 | function dec(x) {
33 | return Math.max(MIN_VALUE, x - 1);
34 | }
35 |
36 | $("#up").click(function () {
37 | var tx = menrva.transaction();
38 | $value.modify(tx, inc);
39 | tx.commit();
40 | });
41 |
42 | $("#down").click(function () {
43 | var tx = menrva.transaction();
44 | $value.modify(tx, dec);
45 | tx.commit();
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/examples/counter/README.md:
--------------------------------------------------------------------------------
1 | Inspired by [simple counter](http://baconjs.github.io/)
2 |
3 | Implemented in *menrva*, [Bacon.js](https://github.com/baconjs/bacon.js) and [RxJS](https://github.com/Reactive-Extensions/RxJS).
4 |
5 | Bacon.js and RxJS variants, come in two flavours, *direct* and *data-flowish*.
6 |
7 |
8 | ```js
9 | $(function () {
10 | "use strict";
11 |
12 | var MIN_VALUE = 0;
13 |
14 | var $value = menrva.source(0);
15 |
16 | $value.onValue(function (x) {
17 | $("#counter").text(x);
18 | });
19 |
20 | $value.onValue(function (x) {
21 | var down = $("#down");
22 | if (x <= MIN_VALUE) {
23 | down.attr("disabled", "disabled");
24 | } else {
25 | down.removeAttr("disabled");
26 | }
27 | })
28 |
29 | function inc(x) {
30 | return x + 1;
31 | }
32 |
33 | function dec(x) {
34 | return Math.max(MIN_VALUE, x - 1);
35 | }
36 |
37 | $("#up").click(function () {
38 | var tx = menrva.transaction();
39 | $value.modify(tx, inc);
40 | tx.commit();
41 | });
42 |
43 | $("#down").click(function () {
44 | var tx = menrva.transaction();
45 | $value.modify(tx, dec);
46 | tx.commit();
47 | });
48 | });
49 | ```
50 |
--------------------------------------------------------------------------------
/examples/suggestions/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "menrva",
3 | "description": "Ambitious data-flow library",
4 | "version": "0.0.7",
5 | "homepage": "https://github.com/phadej/menrva",
6 | "author": {
7 | "name": "Oleg Grenrus",
8 | "email": "oleg.grenrus@iki.fi",
9 | "url": "http://oleg.fi"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git://github.com/phadej/menrva.git"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/phadej/menrva/issues"
17 | },
18 | "licenses": [
19 | {
20 | "type": "MIT",
21 | "url": "https://github.com/phadej/menrva/blob/master/LICENSE"
22 | }
23 | ],
24 | "main": "src/menrva.js",
25 | "engines": {
26 | "node": ">= 0.10.0"
27 | },
28 | "scripts": {
29 | "test": "make test"
30 | },
31 | "devDependencies": {
32 | "uglify-js": "~2.4.14",
33 | "typify-bin": "0.0.5",
34 | "jshint": "~2.5.1",
35 | "istanbul": "~0.3.0",
36 | "mocha": "~1.21.0",
37 | "browserify": "~5.3.0",
38 | "ljs": "~0.2.3",
39 | "coveralls": "~2.11.0",
40 | "lodash": "~2.4.1",
41 | "chai": "~1.9.1",
42 | "benchmark": "~1.0.0",
43 | "rx": "2.3.0",
44 | "baconjs": "0.7.34",
45 | "bacon.model": "0.1.10",
46 | "jsstana": "0.0.21",
47 | "estraverse": "~1.5.0",
48 | "esprima": "~1.2.2",
49 | "sinon": "~1.10.2"
50 | },
51 | "keywords": [
52 | "data",
53 | "flow",
54 | "reactive"
55 | ]
56 | }
57 |
--------------------------------------------------------------------------------
/test/option-test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var menrva = require("../src/menrva.js");
4 | var assert = require("assert");
5 | var _ = require("lodash");
6 |
7 | describe("Option", function () {
8 | describe("map", function () {
9 | describe("is a functor map", function () {
10 | it("— identity law", function () {
11 | var values = [
12 | menrva.some(1),
13 | menrva.some(2),
14 | menrva.none,
15 | ];
16 |
17 | _.each(values, function (v) {
18 | assert(v.map(_.identity).equals(v));
19 | });
20 | });
21 |
22 | it("— composition law", function () {
23 | function f(x) {
24 | return x + 1;
25 | }
26 |
27 | function g(x) {
28 | return x * 2;
29 | }
30 |
31 | var values = [
32 | menrva.some(1),
33 | menrva.some(2),
34 | menrva.none,
35 | ];
36 |
37 | _.each(values, function (v) {
38 | assert(v.map(f).map(g).equals(v.map(_.compose(g, f))));
39 | });
40 | });
41 | });
42 | });
43 |
44 | describe("orElse", function () {
45 | it("unwraps options", function () {
46 | var e = 5;
47 | var values = [
48 | [menrva.some(1), 1],
49 | [menrva.some(2), 2],
50 | [menrva.none, e],
51 | ];
52 |
53 | _.each(values, function (p) {
54 | var v = p[0];
55 | var r = p[1];
56 |
57 | assert(v.orElse(e) === r);
58 | });
59 | });
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/examples/autocomplete/autocomplete-rxjs.js:
--------------------------------------------------------------------------------
1 | $(function () {
2 | function searchWikipedia (term) {
3 | return $.ajax({
4 | url: "http://en.wikipedia.org/w/api.php",
5 | dataType: "jsonp",
6 | data: {
7 | action: "opensearch",
8 | format: "json",
9 | search: window.encodeURI(term)
10 | }
11 | }).promise();
12 | }
13 |
14 | function main() {
15 | var $input = $("#searchtext"),
16 | $results = $("#results");
17 |
18 | // Get all distinct key up events from the input and only fire if long enough and distinct
19 | var keyup = Rx.Observable.fromEvent($input[0], "keyup")
20 | .map(function (e) {
21 | return e.target.value; // Project the text from the input
22 | })
23 | .filter(function (text) {
24 | return text.length > 2; // Only if the text is longer than 2 characters
25 | })
26 | .throttle(750 /* Pause for 750ms */ )
27 | .distinctUntilChanged(); // Only if the value has changed
28 |
29 | var searcher = keyup.flatMapLatest(searchWikipedia);
30 |
31 | /* var subscription = */
32 | searcher.subscribe(
33 | function (data) {
34 | var res = data[1];
35 |
36 | // Append the results
37 | $results.empty();
38 |
39 | $.each(res, function (_, value) {
40 | $("" + value + "").appendTo($results);
41 | });
42 | },
43 | function (error) {
44 | // Handle any errors
45 | $results.empty();
46 |
47 | $("Error: " + error + "").appendTo($results);
48 | });
49 | }
50 |
51 | main();
52 |
53 | });
54 |
--------------------------------------------------------------------------------
/examples/counter/counter-bacon.js:
--------------------------------------------------------------------------------
1 | /**
2 | Inspired by [simple counter](http://baconjs.github.io/)
3 | */
4 | $(function () {
5 | "use strict";
6 |
7 | var MIN_VALUE = 0;
8 |
9 | var upButton = $("#up");
10 | var downButton = $("#down");
11 | var counterEl = $("#counter");
12 |
13 | var $up = upButton.asEventStream("click");
14 | var $down = downButton.asEventStream("click");
15 |
16 | /**
17 | ## Original — direct approach
18 | */
19 | var $counter =
20 | // map up to 1, down to -1
21 | $up.map(1).merge($down.map(-1))
22 | // accumulate sum
23 | .scan(0, function(x,y) { return Math.max(MIN_VALUE, x + y); });
24 |
25 | /**
26 | ## State transformation approach
27 |
28 | This is almost the same as previous. Yet parameters of `scan` are generic.
29 | All of logic is bundled into state transformation functions.
30 | */
31 |
32 | function constant(x) {
33 | return function () {
34 | return x;
35 | };
36 | }
37 |
38 | function inc(x) {
39 | return x + 1;
40 | }
41 |
42 | function dec(x) {
43 | return Math.max(MIN_VALUE, x - 1);
44 | }
45 |
46 | $counter = Bacon.mergeAll($up.map(constant(inc)), $down.map(constant(dec)))
47 | .scan(0, function (state, f) { return f(state); });
48 |
49 | /**
50 | ## Render
51 |
52 | Assign observable value to jQuery property text
53 | */
54 | $counter.assign(counterEl, "text");
55 |
56 | $counter.onValue(function (x) {
57 | if (x <= MIN_VALUE) {
58 | downButton.attr("disabled", "disabled");
59 | } else {
60 | downButton.removeAttr("disabled");
61 | }
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/examples/counter/bacon.md:
--------------------------------------------------------------------------------
1 | Inspired by [simple counter](http://baconjs.github.io/)
2 |
3 |
4 | ```js
5 | $(function () {
6 | "use strict";
7 |
8 | var MIN_VALUE = 0;
9 |
10 | var upButton = $("#up");
11 | var downButton = $("#down");
12 | var counterEl = $("#counter");
13 |
14 | var $up = upButton.asEventStream("click");
15 | var $down = downButton.asEventStream("click");
16 | ```
17 |
18 |
19 | ## Original — direct approach
20 |
21 |
22 | ```js
23 | var $counter =
24 | // map up to 1, down to -1
25 | $up.map(1).merge($down.map(-1))
26 | // accumulate sum
27 | .scan(0, function(x,y) { return Math.max(MIN_VALUE, x + y); });
28 | ```
29 |
30 |
31 | ## State transformation approach
32 |
33 | This is almost the same as previous. Yet parameters of `scan` are generic.
34 | All of logic is bundled into state transformation functions.
35 |
36 |
37 | ```js
38 | function constant(x) {
39 | return function () {
40 | return x;
41 | };
42 | }
43 |
44 | function inc(x) {
45 | return x + 1;
46 | }
47 |
48 | function dec(x) {
49 | return Math.max(MIN_VALUE, x - 1);
50 | }
51 |
52 | $counter = Bacon.mergeAll($up.map(constant(inc)), $down.map(constant(dec)))
53 | .scan(0, function (state, f) { return f(state); })
54 | ```
55 |
56 |
57 | ## Render
58 |
59 | Assign observable value to jQuery property text
60 |
61 |
62 | ```js
63 | $counter.assign(counterEl, "text");
64 |
65 | $counter.onValue(function (x) {
66 | if (x <= MIN_VALUE) {
67 | downButton.attr("disabled", "disabled");
68 | } else {
69 | downButton.removeAttr("disabled");
70 | }
71 | });
72 | });
73 | ```
74 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all : test
2 |
3 | SRC=src/*.js
4 | TESTSRC=test
5 |
6 | DISTDIR=dist
7 | DISTPREFIX=menrva
8 |
9 | BUNDLESRC=src/menrva.js
10 | BUNDLEDST=$(DISTDIR)/$(DISTPREFIX).standalone.js
11 | BUNDLEVAR=menrva
12 |
13 | MINSRC=$(BUNDLEDST)
14 | MINDST=$(DISTDIR)/$(DISTPREFIX).min.js
15 | MINMAP=$(DISTDIR)/$(DISTPREFIX).min.js.map
16 |
17 | LJSSRC=src/menrva.js
18 |
19 | .PHONY : all test jshint mocha istanbul browserify typify literate version dist
20 |
21 | BINDIR=node_modules/.bin
22 |
23 | MOCHA=$(BINDIR)/_mocha
24 | ISTANBUL=$(BINDIR)/istanbul
25 | JSHINT=$(BINDIR)/jshint
26 | BROWSERIFY=$(BINDIR)/browserify
27 | UGLIFY=$(BINDIR)/uglifyjs
28 | TYPIFY=$(BINDIR)/typify
29 | LJS=$(BINDIR)/ljs
30 | COVERALLS=$(BINDIR)/coveralls
31 |
32 | test : jshint mocha istanbul typify
33 |
34 | jshint :
35 | $(JSHINT) $(SRC)
36 | $(JSHINT) benchmark
37 | $(JSHINT) -c .jshintrc.examples examples
38 |
39 | mocha :
40 | $(MOCHA) --reporter=spec $(TESTSRC)
41 |
42 | istanbul :
43 | $(ISTANBUL) cover $(MOCHA) $(TESTSRC)
44 | $(ISTANBUL) check-coverage --statements 100 --branches 100 --functions 100
45 |
46 | browserify : $(SRC)
47 | mkdir -p $(DISTDIR)
48 | $(BROWSERIFY) -s $(BUNDLEVAR) -o $(BUNDLEDST) $(BUNDLESRC)
49 |
50 | uglify : browserify $(SRC)
51 | mkdir -p $(DISTDIR)
52 | $(UGLIFY) -o $(MINDST) --source-map $(MINMAP) $(MINSRC)
53 |
54 | typify :
55 | $(TYPIFY) $(MOCHA) $(TESTSRC)
56 |
57 | literate :
58 | $(LJS) -c false -o README.md $(LJSSRC)
59 |
60 | coveralls :
61 | if [ ! -z `node --version | grep v0.10` ]; then cat ./coverage/lcov.info | $(COVERALLS); fi
62 |
63 | version:
64 | node scripts/version.js
65 |
66 | dist : test version uglify literate
67 | git clean -fdx -e node_modules
68 |
--------------------------------------------------------------------------------
/test/menrva-test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var menrva = require("../src/menrva.js");
4 | var assert = require("assert");
5 | var _ = require("lodash");
6 | var chai = require("chai");
7 |
8 | describe("menrva", function () {
9 | it("was an Etruscan goddess of war, art, wisdom and health.", function () {
10 | assert(true);
11 | });
12 | });
13 |
14 | describe("triangle shape", function () {
15 | /*
16 | a = source()
17 | b = a + 1
18 | c = a + b = 2 * a + 1
19 | */
20 | var a, b, c;
21 | beforeEach(function () {
22 | a = menrva.source(1);
23 | b = a.map(function (x) { return x + 1; });
24 | c = menrva.combine(a, b, function (x, y) {
25 | return x + y;
26 | });
27 | });
28 |
29 | describe("initial case", function () {
30 | it("value is calculated already", function () {
31 | chai.expect(c.value()).to.equal(3);
32 | });
33 |
34 | it("when adding onValue callback, it's executed synchronously", function () {
35 | var x = 2;
36 | var count = 0;
37 | c.onValue(function (y) {
38 | x = y;
39 | count += 1;
40 | });
41 | chai.expect(x).to.equal(3);
42 | chai.expect(count).to.equal(1);
43 | });
44 | });
45 |
46 | it("updating", function () {
47 | var count = 0;
48 | c.onValue(function (y) {
49 | count += 1;
50 | });
51 |
52 | chai.expect(c.value()).to.equal(3);
53 | chai.expect(count).to.equal(1);
54 |
55 | var tx = menrva.transaction();
56 | a.set(tx, 2);
57 |
58 | chai.expect(c.value()).to.equal(3);
59 | chai.expect(count).to.equal(1);
60 |
61 | tx.commit();
62 |
63 | chai.expect(c.value()).to.equal(5);
64 | chai.expect(count).to.equal(2);
65 | });
66 | });
67 |
--------------------------------------------------------------------------------
/src/util.js:
--------------------------------------------------------------------------------
1 | /*
2 | * menrva
3 | * https://github.com/phadej/menrva
4 | *
5 | * Copyright (c) 2014 Oleg Grenrus
6 | * Licensed under the MIT license.
7 | */
8 |
9 | "use strict";
10 |
11 | function identity(x) {
12 | return x;
13 | }
14 |
15 | function pluck(arr, property) {
16 | var len = arr.length;
17 | var res = new Array(len);
18 | for (var i = 0; i < len; i++) {
19 | res[i] = arr[i][property];
20 | }
21 | return res;
22 | }
23 |
24 | function values(obj) {
25 | var arr = [];
26 | for (var k in obj) {
27 | arr.push(obj[k]);
28 | }
29 | return arr;
30 | }
31 |
32 | function objIsEmpty(obj) {
33 | /* jshint unused:false */
34 | for (var k in obj) {
35 | return false;
36 | }
37 | return true;
38 | }
39 |
40 | function getPath(obj, path) {
41 | var len = path.length;
42 | for (var i = 0; i < len; i++) {
43 | if (obj === undefined || obj === null) {
44 | return obj;
45 | } else {
46 | obj = obj[path[i]];
47 | }
48 | }
49 | return obj;
50 | }
51 |
52 | function setProperty(obj, property, value) {
53 | var copy = {};
54 | for (var k in obj) {
55 | copy[k] = obj[k];
56 | }
57 | copy[property] = value;
58 | return copy;
59 | }
60 |
61 | function setPath(obj, path, value) {
62 | var len = path.length;
63 | var acc = value;
64 | for (var i = len; i > 0; i--) {
65 | var next = getPath(obj, path.slice(0, i - 1));
66 | acc = setProperty(next, path[i - 1], acc);
67 | }
68 | return acc;
69 | }
70 |
71 | function modifyPath(obj, path, f) {
72 | return setPath(obj, path, f(getPath(obj, path)));
73 | }
74 |
75 | module.exports = {
76 | identity: identity,
77 | pluck: pluck,
78 | values: values,
79 | objIsEmpty: objIsEmpty,
80 | getPath: getPath,
81 | setPath: setPath,
82 | modifyPath: modifyPath,
83 | };
84 |
--------------------------------------------------------------------------------
/src/lens.js:
--------------------------------------------------------------------------------
1 | /*
2 | * menrva
3 | * https://github.com/phadej/menrva
4 | *
5 | * Copyright (c) 2014 Oleg Grenrus
6 | * Licensed under the MIT license.
7 | */
8 |
9 | "use strict";
10 |
11 | var util = require("./util.js");
12 | var signal = require("./signal.js");
13 |
14 | /**
15 | ### Lens
16 |
17 | Lenses are composable functional references.
18 | They allow you to *access* and *modify* data potentially very deep within a structure!
19 | */
20 | function Lens(parent, path, eq) {
21 | this.parents = [parent];
22 | this.path = path.split(/\./);
23 | var value = this.calculate();
24 | signal.initSignal(this, value, eq);
25 | }
26 |
27 | Lens.prototype = new signal.Signal();
28 |
29 | Lens.prototype.calculateRank = function () {
30 | return this.parents[0].rank + 1;
31 | };
32 |
33 | Lens.prototype.calculate = function () {
34 | return util.getPath(this.parents[0].v, this.path);
35 | };
36 |
37 | /**
38 | #### source.zoom
39 |
40 | > zoom (@ : Source a, path : Path a b, eq = egal : b -> b -> boolean) : Source b
41 |
42 | Zoom (or focus) into part specified by `path` of the original signal.
43 | One can `set` and `modify` zoomed signals, they act as sources.
44 |
45 | ```js
46 | var quux = source.zoom("foo.bar.quux");
47 | ```
48 | */
49 | signal.Source.prototype.zoom = Lens.prototype.zoom = function(f, eq) {
50 | var mapped = new Lens(this, f, eq);
51 | this.children.push(mapped);
52 | return mapped;
53 | };
54 |
55 | Lens.prototype.set = function (tx, value) {
56 | var path = this.path;
57 | this.parents[0].modify(tx, function (obj) {
58 | return util.setPath(obj, path, value);
59 | });
60 | };
61 |
62 | Lens.prototype.modify = function (tx, f) {
63 | var path = this.path;
64 | this.parents[0].modify(tx, function (obj) {
65 | return util.modifyPath(obj, path, f);
66 | });
67 | };
68 |
--------------------------------------------------------------------------------
/src/option.js:
--------------------------------------------------------------------------------
1 | /*
2 | * menrva
3 | * https://github.com/phadej/menrva
4 | *
5 | * Copyright (c) 2014 Oleg Grenrus
6 | * Licensed under the MIT license.
7 | */
8 |
9 | "use strict";
10 |
11 | var egal = require("./egal.js");
12 | var util = require("./util.js");
13 |
14 | // typify: instance Option
15 | // Option type
16 |
17 | /**
18 | ### Option
19 |
20 | Also known as `Maybe`.
21 | */
22 | function Option() {}
23 |
24 | // Some
25 | function Some(x) {
26 | this.value = x;
27 | }
28 |
29 | Some.prototype = new Option();
30 |
31 | function some(x) {
32 | return new Some(x);
33 | }
34 |
35 | // None
36 | function None() {}
37 |
38 | None.prototype = new Option();
39 |
40 | var none = new None();
41 |
42 | // Methods
43 |
44 | /**
45 | #### option.equals
46 |
47 | > equals (@ : option a, other : *, eq = eqal : a -> a -> boolean) : boolean
48 |
49 | Equality check.
50 | */
51 | Some.prototype.equals = function (other, eq) {
52 | eq = eq || egal;
53 | return other instanceof Some && eq(this.value, other.value); // TODO: use egal
54 | };
55 |
56 | None.prototype.equals = function (other) {
57 | // only one instance of `none`
58 | return this === other;
59 | };
60 |
61 | /**
62 | #### option.map
63 |
64 | > map (@ : option a, f : a -> b) : option b
65 | */
66 | // :: fn -> Option
67 | Some.prototype.map = function (f) {
68 | return some(f(this.value));
69 | };
70 |
71 | // :: fn -> Option
72 | None.prototype.map = function (f) {
73 | return none;
74 | };
75 |
76 | /**
77 | #### option.elim
78 |
79 | > elim (@ : option a, x : b, f : a -> b) : b
80 |
81 | */
82 | Some.prototype.elim = function (x, f) {
83 | return f(this.value);
84 | };
85 |
86 | None.prototype.elim = function (x, f) {
87 | return x;
88 | };
89 |
90 | /**
91 | #### option.orElse
92 |
93 | > orElse (@ : option a, x : a) : a
94 | */
95 | Option.prototype.orElse = function (x) {
96 | return this.elim(x, util.identity);
97 | };
98 |
99 | module.exports = {
100 | some: some,
101 | none: none,
102 | };
103 |
--------------------------------------------------------------------------------
/src/menrva.js:
--------------------------------------------------------------------------------
1 | /*
2 | * menrva
3 | * https://github.com/phadej/menrva
4 | *
5 | * Copyright (c) 2014 Oleg Grenrus
6 | * Licensed under the MIT license.
7 | */
8 |
9 | /**
10 | # menrva
11 |
12 |
13 |
14 | [](http://travis-ci.org/phadej/menrva)
15 | [](https://www.npmjs.org/package/menrva)
16 | [](https://david-dm.org/phadej/menrva)
17 | [](https://david-dm.org/phadej/menrva#info=devDependencies)
18 | [](https://coveralls.io/r/phadej/menrva?branch=master)
19 | [](https://codeclimate.com/github/phadej/menrva)
20 |
21 | Ambitious data-flow library.
22 |
23 | ## Getting Started
24 | Install the module with: `npm install menrva`
25 |
26 | ```js
27 | var menrva = require('menrva');
28 | menrva.some('awe'); // some, as in awesome?
29 | ```
30 |
31 | ## API
32 | */
33 | /// include signal.js
34 | /// include transaction.js
35 | /// include lens.js
36 | /// include convenience.js
37 | /// include egal.js
38 | /// include option.js
39 | /**
40 | ## Contributing
41 | */
42 | /// plain ../CONTRIBUTING.md
43 | /**
44 | ## Release History
45 | */
46 | /// plain ../CHANGELOG.md
47 | /**
48 |
49 | ## License
50 |
51 | Copyright (c) 2014 Oleg Grenrus.
52 | Licensed under the MIT license.
53 | */
54 |
55 | "use strict";
56 |
57 | var egal = require("./egal.js");
58 | var option = require("./option.js");
59 | var signal = require("./signal.js");
60 | var transaction = require("./transaction.js");
61 |
62 | // extensions
63 | require("./lens.js");
64 | var convenience = require("./convenience.js");
65 |
66 | // version
67 | var version = "0.0.7";
68 |
69 | module.exports = {
70 | egal: egal,
71 | some: option.some,
72 | none: option.none,
73 | Signal: signal.Signal,
74 | source: signal.source,
75 | combine: signal.combine,
76 | tuple: convenience.tuple,
77 | sequence: convenience.sequence,
78 | record: convenience.record,
79 | transaction: transaction,
80 | version: version,
81 | };
82 |
--------------------------------------------------------------------------------
/test/signal-test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var menrva = require("../src/menrva.js");
4 | var assert = require("assert");
5 | var _ = require("lodash");
6 | var chai = require("chai");
7 |
8 | describe("signal", function () {
9 | /*
10 | a = source()
11 | b = a + 1
12 | c = a + b = 2 * a + 1
13 | */
14 | var a, b, c;
15 | beforeEach(function () {
16 | a = menrva.source(1);
17 | b = a.map(function (x) { return x + 1; });
18 | c = menrva.combine(a, b, function (x, y) {
19 | return x + y;
20 | });
21 | });
22 |
23 | describe("modify", function () {
24 | it("makes possible to modify current value of the signal", function () {
25 | var count = 0;
26 | c.onValue(function (y) {
27 | count += 1;
28 | });
29 |
30 | chai.expect(c.value()).to.equal(3);
31 | chai.expect(count).to.equal(1);
32 |
33 | var tx1 = menrva.transaction();
34 | a.set(tx1, 2);
35 | a.modify(tx1, function (x) { return x * x; });
36 | tx1.commit();
37 |
38 | chai.expect(c.value()).to.equal(9);
39 | chai.expect(count).to.equal(2);
40 |
41 | var tx2 = menrva.transaction();
42 | a.modify(tx2, function (x) { return x * x; });
43 | tx2.commit();
44 |
45 | chai.expect(c.value()).to.equal(33);
46 | chai.expect(count).to.equal(3);
47 | });
48 | });
49 |
50 | describe("unsubscriber", function () {
51 | it("let you unsubscribe, duh!", function () {
52 | var count = 0;
53 | var unsub = c.onValue(function (y) {
54 | count += 1;
55 | });
56 |
57 | chai.expect(c.value()).to.equal(3);
58 | chai.expect(count).to.equal(1);
59 |
60 | var tx1 = menrva.transaction();
61 | a.set(tx1, 2);
62 | tx1.commit();
63 |
64 | chai.expect(c.value()).to.equal(5);
65 | chai.expect(count).to.equal(2);
66 |
67 | unsub();
68 |
69 | var tx2 = menrva.transaction();
70 | a.set(tx2, 3);
71 | tx2.commit();
72 |
73 | chai.expect(c.value()).to.equal(7);
74 | chai.expect(count).to.equal(2);
75 |
76 | // you can unsub many times - second and following calls are no-op
77 | unsub();
78 |
79 | var tx3 = menrva.transaction();
80 | a.set(tx3, 2);
81 | tx3.commit();
82 |
83 | chai.expect(c.value()).to.equal(5);
84 | chai.expect(count).to.equal(2);
85 | });
86 | });
87 | });
88 |
--------------------------------------------------------------------------------
/test/convenience-test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var menrva = require("../src/menrva.js");
4 | var assert = require("assert");
5 | var _ = require("lodash");
6 | var chai = require("chai");
7 | var sinon = require("sinon");
8 |
9 | describe("convenience features", function () {
10 | describe("log", function () {
11 | beforeEach(function () {
12 | sinon.stub(console, "log");
13 | });
14 | afterEach(function () {
15 | console.log.restore();
16 | });
17 |
18 | it("is onValue(console.log)", function () {
19 | var a = menrva.source(1);
20 | a.log("foo", "bar");
21 |
22 | chai.expect(console.log.args).to.deep.equal([["foo", "bar", 1]]);
23 | });
24 | });
25 |
26 | describe("tuple - onSpread", function () {
27 | it("are convenient when you want to react to change in either signal", function () {
28 | var a = menrva.source(1);
29 | var b = menrva.source(2);
30 |
31 | var stub = sinon.stub();
32 |
33 | menrva.tuple(a, b).onSpread(stub);
34 |
35 | menrva.transaction([a, 3]).commit();
36 | menrva.transaction([b, 4]).commit();
37 | menrva.transaction([a, 5, b, 6]).commit();
38 |
39 | chai.expect(stub.args).to.deep.equal([
40 | [1, 2],
41 | [3, 2],
42 | [3, 4],
43 | [5, 6],
44 | ]);
45 | });
46 | });
47 |
48 | describe("sequence", function () {
49 | it("is Traversable method", function () {
50 | var a = menrva.source(1);
51 | var b = menrva.source(2);
52 | var c = menrva.source(3);
53 |
54 | var stub = sinon.stub();
55 |
56 | menrva.sequence([a, b, c]).onValue(stub);
57 |
58 | menrva.transaction([a, 4]).commit();
59 | menrva.transaction([b, 5, c, 6]).commit();
60 |
61 | chai.expect(stub.args).to.deep.equal([
62 | [[1, 2, 3]],
63 | [[4, 2, 3]],
64 | [[4, 5, 6]],
65 | ]);
66 | });
67 | });
68 |
69 | describe("record", function () {
70 | it("is Traversable method", function () {
71 | var a = menrva.source(1);
72 | var b = menrva.source(2);
73 | var c = menrva.source(3);
74 |
75 | var stub = sinon.stub();
76 |
77 | menrva.record({k: a, l: b, m: c}).onValue(stub);
78 |
79 | menrva.transaction([a, 4]).commit();
80 | menrva.transaction([b, 5, c, 6]).commit();
81 |
82 | chai.expect(stub.args).to.deep.equal([
83 | [{k: 1, l: 2, m: 3}],
84 | [{k: 4, l: 2, m: 3}],
85 | [{k: 4, l: 5, m: 6}],
86 | ]);
87 | });
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/examples/autocomplete/autocomplete.js:
--------------------------------------------------------------------------------
1 | /**
2 | Inspired by [RxJS autocomplete example](https://github.com/Reactive-Extensions/RxJS/tree/master/examples/autocomplete)
3 | */
4 | $(function () {
5 | "use strict";
6 |
7 | var inputElement = $("#searchtext");
8 | var resultsElement = $("#results");
9 |
10 | var $searchText = menrva.source(inputElement.val());
11 | var $searchCache = menrva.source({}, _.isEqual);
12 | var $searchResult = menrva.combine($searchText, $searchCache, function (searchText, searchCache) {
13 | return searchCache[searchText];
14 | });
15 |
16 | var LOADING = "loading...";
17 |
18 | // update $searchText
19 | inputElement.keyup(function (ev) {
20 | var text = ev.target.value;
21 | menrva.transaction()
22 | .set($searchText, text)
23 | .commit();
24 | });
25 |
26 | $searchResult.onValue(function (searchResult) {
27 | if (searchResult) {
28 | resultsElement.empty();
29 |
30 | if (searchResult === LOADING) {
31 | resultsElement.html("Loading...");
32 | } else {
33 | $.each(searchResult, function (key, value) {
34 | $("").text(value).appendTo(resultsElement);
35 | });
36 | }
37 | }
38 | });
39 |
40 | // Search Wikipedia for a given term
41 | function searchWikipedia (term) {
42 | // set we are searching the term
43 | menrva.transaction()
44 | .modify($searchCache, function (searchCache) {
45 | var add = {};
46 | add[term] = LOADING;
47 | return _.extend({}, searchCache, add);
48 | })
49 | .commit();
50 |
51 | // Perform the fetch
52 | return $.ajax({
53 | url: "http://en.wikipedia.org/w/api.php",
54 | dataType: "jsonp",
55 | data: {
56 | action: "opensearch",
57 | format: "json",
58 | search: window.encodeURI(term)
59 | }
60 | })
61 | .then(function (searchResult) {
62 | menrva.transaction()
63 | .modify($searchCache, function (searchCache) {
64 | var newCache = _.extend({}, searchCache);
65 | newCache[term] = searchResult[1];
66 | return newCache;
67 | })
68 | .commit();
69 | });
70 | }
71 |
72 | var throttledSearchWikipedia = _.throttle(searchWikipedia, 750);
73 |
74 | // if there isn't result, perform it.
75 | menrva.tuple($searchText, $searchResult).onSpread(function (searchText, searchResult) {
76 | if (searchText.length > 2 && !searchResult) {
77 | throttledSearchWikipedia(searchText);
78 | }
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/src/convenience.js:
--------------------------------------------------------------------------------
1 | /*
2 | * menrva
3 | * https://github.com/phadej/menrva
4 | *
5 | * Copyright (c) 2014 Oleg Grenrus
6 | * Licensed under the MIT license.
7 | */
8 |
9 | "use strict";
10 |
11 | var signal = require("./signal.js");
12 | var util = require("./util.js");
13 |
14 | /**
15 | ### Convenience methods
16 |
17 | #### signal.log
18 |
19 | > signal.log (@ : Signal a, args...) : Unsubscriber
20 |
21 | Essentially `signal.onValue(console.log.bind(console, args...))
22 | */
23 | signal.Signal.prototype.log = function () {
24 | var args = Array.prototype.slice.call(arguments);
25 | return this.onValue(function (x) {
26 | var logArgs = args.concat([x]);
27 | console.log.apply(console, logArgs);
28 | });
29 | };
30 |
31 | /**
32 | #### signal.onSpread
33 |
34 | > signal.onSpread (@ : Signal [a, b...], callback : a -> b ... -> void) : Unsubscriber
35 |
36 | `onValue` with signal's tuple arguments spread.
37 | */
38 | signal.Signal.prototype.onSpread = function (callback) {
39 | return this.onValue(function (t) {
40 | callback.apply(undefined, t);
41 | });
42 | };
43 |
44 | /**
45 | #### tuple
46 |
47 | > tuple (x : Signal a, y : Signal b...) : Signal [a, b...]
48 |
49 | Combine signals into tuple.
50 | */
51 |
52 | function tuple() {
53 | var signals = Array.prototype.slice.call(arguments);
54 | var mapped = new (signal.CombinedSignal)(signals, util.identity);
55 |
56 | // connect to parent
57 | signals.forEach(function (parent) {
58 | parent.children.push(mapped);
59 | });
60 |
61 | return mapped;
62 | }
63 |
64 | /**
65 | #### sequence
66 |
67 | > sequence [Signal a, Signal b, ..] : Signal [a, b...]
68 |
69 | In promise libraries this might be called `all`.
70 | */
71 | function sequence(signals) {
72 | var mapped = new (signal.CombinedSignal)(signals, util.identity);
73 |
74 | // connect to parent
75 | signals.forEach(function (parent) {
76 | parent.children.push(mapped);
77 | });
78 |
79 | return mapped;
80 | }
81 |
82 | /**
83 | #### record
84 |
85 | > record {k: Signal a, l: Signal b...} : Signal {k: a, l: b...}
86 |
87 | Like `sequence` but for records i.e. objects.
88 | */
89 |
90 | function record(rec) {
91 | var keys = [];
92 | var signals = [];
93 |
94 | for (var k in rec) {
95 | // if (Object.prototype.hasOwnProperty.call(rec, k)) {
96 | keys.push(k);
97 | signals.push(rec[k]);
98 | // }
99 | }
100 |
101 | function toObject(values) {
102 | var res = {};
103 |
104 | for (var i = 0; i < keys.length; i++) {
105 | res[keys[i]] = values[i];
106 | }
107 |
108 | return res;
109 | }
110 |
111 | var mapped = new (signal.CombinedSignal)(signals, toObject);
112 |
113 | // connect to parent
114 | signals.forEach(function (parent) {
115 | parent.children.push(mapped);
116 | });
117 |
118 | return mapped;
119 | }
120 |
121 | module.exports = {
122 | tuple: tuple,
123 | sequence: sequence,
124 | record: record,
125 | };
126 |
--------------------------------------------------------------------------------
/examples/suggestions/README.md:
--------------------------------------------------------------------------------
1 | Inspired by [*The introduction to Reactive Programming you've been missing*](http://jsfiddle.net/staltz/8jFJH/48) by [@andrestaltz](https://twitter.com/andrestaltz)
2 |
3 | This is [literated](https://github.com/phadej/ljs) javascript file.
4 |
5 | We start with usual jQuery prelude:
6 |
7 |
8 | ```js
9 | $(function () {
10 | "use strict";
11 | ```
12 |
13 |
14 | ## Model
15 |
16 | The global state of this small widget consists of four variables: list of users and three random variables.
17 |
18 | As *signals* always have a value, we initialize the list of users with an empty list.
19 |
20 | As a notation, we use `$foo` for signals, and `foo` for their values.
21 | One may call this [Hungarian notation](http://en.wikipedia.org/wiki/Hungarian_notation).
22 |
23 |
24 | ```js
25 | var $users = menrva.source([]);
26 | var $fst = menrva.source(Math.random());
27 | var $snd = menrva.source(Math.random());
28 | var $trd = menrva.source(Math.random());
29 | ```
30 |
31 |
32 | ## Derived data
33 |
34 | After we identified the essential data pieces, everything else can be derived from them.
35 |
36 | We `combine` signals to get specific user for each slot in our widget.
37 |
38 |
39 | ```js
40 | function selectUser(users, rnd) {
41 | if (users.length === 0) {
42 | return undefined;
43 | } else {
44 | return users[Math.floor(rnd * users.length)];
45 | }
46 | }
47 |
48 | var $fstUser = menrva.combine($users, $fst, selectUser);
49 | var $sndUser = menrva.combine($users, $snd, selectUser);
50 | var $trdUser = menrva.combine($users, $trd, selectUser);
51 | ```
52 |
53 |
54 | ## Rendering
55 |
56 | Showing the suggestions is now easy. We only have to attach `onValue` callbacks to the user signals.
57 |
58 | `renderSuggestion` is exactly the same as in original jsfiddle.
59 |
60 |
61 | ```js
62 | function renderSuggestion(suggestedUser, selector) {
63 | var suggestionEl = document.querySelector(selector);
64 | if (suggestedUser === undefined) {
65 | suggestionEl.style.visibility = 'hidden';
66 | } else {
67 | suggestionEl.style.visibility = 'visible';
68 | var usernameEl = suggestionEl.querySelector('.username');
69 | usernameEl.href = suggestedUser.html_url;
70 | usernameEl.textContent = suggestedUser.login;
71 | var imgEl = suggestionEl.querySelector('img');
72 | imgEl.src = "";
73 | imgEl.src = suggestedUser.avatar_url;
74 | }
75 | }
76 |
77 | $fstUser.onValue(function (user) {
78 | renderSuggestion(user, '.suggestion1');
79 | });
80 |
81 | $sndUser.onValue(function (user) {
82 | renderSuggestion(user, '.suggestion2');
83 | });
84 |
85 | $trdUser.onValue(function (user) {
86 | renderSuggestion(user, '.suggestion3');
87 | });
88 | ```
89 |
90 |
91 | ## Events
92 |
93 | Events make our system tick. And here is the difference to RxJs implementation.
94 | *menrva* doesn't have any tools for working with events.
95 | This is intentional choice. For events one may use anything one likes.
96 | Pure callbacks, promises, even RxJS (it's awesome for event networks).
97 |
98 | As in this example event network is simple, we handle them directly.
99 |
100 | There are two event types: refresh and close. On refresh we fetch new user list,
101 | and set the value of `$users`.
102 |
103 | Here we use promises directly.
104 |
105 | For handling one-to-one asynchronicity promises are very good abstraction.
106 | *menrva*, on the other hand, handles reactiviness.
107 |
108 |
109 | ```js
110 | function refresh() {
111 | var randomOffset = Math.floor(Math.random()*500);
112 | $.getJSON("https://api.github.com/users?since=" + randomOffset).then(function (response) {
113 | var tx = menrva.transaction();
114 | $users.set(tx, response);
115 | tx.commit();
116 | });
117 | }
118 |
119 | function randomize(source) {
120 | return function () {
121 | var tx = menrva.transaction();
122 | source.set(tx, Math.random());
123 | tx.commit();
124 | };
125 | }
126 |
127 | // Initial populate
128 | refresh();
129 |
130 | // Event bindings
131 | var refreshButton = $('.refresh');
132 | var closeButton1 = $('.close1');
133 | var closeButton2 = $('.close2');
134 | var closeButton3 = $('.close3');
135 |
136 | refreshButton.click(refresh);
137 |
138 | closeButton1.click(randomize($fst));
139 | closeButton2.click(randomize($snd));
140 | closeButton3.click(randomize($trd));
141 | });
142 | ```
143 |
--------------------------------------------------------------------------------
/examples/suggestions/suggestions.js:
--------------------------------------------------------------------------------
1 | /**
2 | Inspired by [*The introduction to Reactive Programming you've been missing*](http://jsfiddle.net/staltz/8jFJH/48) by [@andrestaltz](https://twitter.com/andrestaltz)
3 |
4 | This is [literated](https://github.com/phadej/ljs) javascript file.
5 |
6 | We start with usual jQuery prelude:
7 | */
8 | $(function () {
9 | "use strict";
10 |
11 | /**
12 | ## Model
13 |
14 | The global state of this small widget consists of four variables: list of users and three random variables.
15 |
16 | As *signals* always have a value, we initialize the list of users with an empty list.
17 |
18 | As a notation, we use `$foo` for signals, and `foo` for their values.
19 | One may call this [Hungarian notation](http://en.wikipedia.org/wiki/Hungarian_notation).
20 | */
21 | var $users = menrva.source([]);
22 | var $fst = menrva.source(Math.random());
23 | var $snd = menrva.source(Math.random());
24 | var $trd = menrva.source(Math.random());
25 |
26 | /**
27 | ## Derived data
28 |
29 | After we identified the essential data pieces, everything else can be derived from them.
30 |
31 | We `combine` signals to get specific user for each slot in our widget.
32 | */
33 | function selectUser(users, rnd) {
34 | if (users.length === 0) {
35 | return undefined;
36 | } else {
37 | return users[Math.floor(rnd * users.length)];
38 | }
39 | }
40 |
41 | var $fstUser = menrva.combine($users, $fst, selectUser);
42 | var $sndUser = menrva.combine($users, $snd, selectUser);
43 | var $trdUser = menrva.combine($users, $trd, selectUser);
44 |
45 | /**
46 | ## Rendering
47 |
48 | Showing the suggestions is now easy. We only have to attach `onValue` callbacks to the user signals.
49 |
50 | `renderSuggestion` is exactly the same as in original jsfiddle.
51 | */
52 |
53 | function renderSuggestion(suggestedUser, selector) {
54 | var suggestionEl = document.querySelector(selector);
55 | if (suggestedUser === undefined) {
56 | suggestionEl.style.visibility = "hidden";
57 | } else {
58 | suggestionEl.style.visibility = "visible";
59 | var usernameEl = suggestionEl.querySelector(".username");
60 | usernameEl.href = suggestedUser["html_url"];
61 | usernameEl.textContent = suggestedUser["login"];
62 | var imgEl = suggestionEl.querySelector("img");
63 | imgEl.src = "";
64 | imgEl.src = suggestedUser["avatar_url"];
65 | }
66 | }
67 |
68 | $fstUser.onValue(function (user) {
69 | renderSuggestion(user, ".suggestion1");
70 | });
71 |
72 | $sndUser.onValue(function (user) {
73 | renderSuggestion(user, ".suggestion2");
74 | });
75 |
76 | $trdUser.onValue(function (user) {
77 | renderSuggestion(user, ".suggestion3");
78 | });
79 |
80 | /**
81 | ## Events
82 |
83 | Events make our system tick. And here is the difference to RxJs implementation.
84 | *menrva* doesn't have any tools for working with events.
85 | This is intentional choice. For events one may use anything one likes.
86 | Pure callbacks, promises, even RxJS (it's awesome for event networks).
87 |
88 | As in this example event network is simple, we handle them directly.
89 |
90 | There are two event types: refresh and close. On refresh we fetch new user list,
91 | and set the value of `$users`.
92 |
93 | Here we use promises directly.
94 |
95 | For handling one-to-one asynchronicity promises are very good abstraction.
96 | *menrva*, on the other hand, handles reactiviness.
97 | */
98 |
99 | function refresh() {
100 | var randomOffset = Math.floor(Math.random()*500);
101 | $.getJSON("https://api.github.com/users?since=" + randomOffset).then(function (response) {
102 | var tx = menrva.transaction();
103 | $users.set(tx, response);
104 | tx.commit();
105 | });
106 | }
107 |
108 | function randomize(source) {
109 | return function () {
110 | var tx = menrva.transaction();
111 | source.set(tx, Math.random());
112 | tx.commit();
113 | };
114 | }
115 |
116 | // Initial populate
117 | refresh();
118 |
119 | // Event bindings
120 | var refreshButton = $(".refresh");
121 | var closeButton1 = $(".close1");
122 | var closeButton2 = $(".close2");
123 | var closeButton3 = $(".close3");
124 |
125 | refreshButton.click(refresh);
126 |
127 | closeButton1.click(randomize($fst));
128 | closeButton2.click(randomize($snd));
129 | closeButton3.click(randomize($trd));
130 | });
131 |
--------------------------------------------------------------------------------
/test/lens-test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var menrva = require("../src/menrva.js");
4 | var assert = require("assert");
5 | var _ = require("lodash");
6 | var chai = require("chai");
7 |
8 | describe("zoom", function () {
9 | var a, b, c, d, e, f;
10 | var bCount, cCount, dCount, eCount, fCount;
11 | beforeEach(function () {
12 | a = menrva.source({
13 | foo: {
14 | bar: 1,
15 | baz: 2,
16 | },
17 | quux: "hello world"
18 | });
19 |
20 | b = a.zoom("foo.bar");
21 | c = a.zoom("quux");
22 | d = a.zoom("not.here");
23 | e = a.zoom("foo", _.isEqual);
24 | f = e.zoom("baz");
25 |
26 | bCount = cCount = dCount = eCount = fCount = 0;
27 | b.onValue(function () { bCount += 1; });
28 | c.onValue(function () { cCount += 1; });
29 | d.onValue(function () { dCount += 1; });
30 | e.onValue(function () { eCount += 1; });
31 | f.onValue(function () { fCount += 1; });
32 | });
33 |
34 | it("get functionality", function () {
35 | chai.expect(b.value()).to.equal(1);
36 | chai.expect(c.value()).to.equal("hello world");
37 | chai.expect(d.value()).to.equal(undefined);
38 | });
39 |
40 | it("set functionality - parent", function () {
41 | chai.expect(bCount).to.equal(1);
42 | chai.expect(cCount).to.equal(1);
43 | chai.expect(dCount).to.equal(1);
44 | chai.expect(eCount).to.equal(1);
45 | chai.expect(fCount).to.equal(1);
46 |
47 | var tx = menrva.transaction();
48 | a.set(tx, {
49 | foo: {
50 | bar: 2,
51 | baz: 2,
52 | },
53 | quux: "hello world"
54 | });
55 | tx.commit();
56 |
57 | chai.expect(b.value()).to.equal(2);
58 | chai.expect(c.value()).to.equal("hello world");
59 | chai.expect(d.value()).to.equal(undefined);
60 | chai.expect(e.value()).to.deep.equal({bar: 2, baz: 2});
61 | chai.expect(f.value()).to.equal(2);
62 |
63 | chai.expect(bCount).to.equal(2);
64 | chai.expect(cCount).to.equal(1);
65 | chai.expect(dCount).to.equal(1);
66 | chai.expect(eCount).to.equal(2);
67 | chai.expect(fCount).to.equal(1);
68 | });
69 |
70 | it("set functionality - thru lens", function () {
71 | var tx = menrva.transaction();
72 | b.set(tx, 3);
73 | tx.commit();
74 |
75 | chai.expect(b.value()).to.equal(3);
76 | chai.expect(e.value()).to.deep.equal({ bar: 3, baz: 2});
77 | chai.expect(f.value()).to.equal(2);
78 |
79 | chai.expect(bCount).to.equal(2);
80 | chai.expect(cCount).to.equal(1);
81 | chai.expect(dCount).to.equal(1);
82 | chai.expect(eCount).to.equal(2);
83 | chai.expect(fCount).to.equal(1);
84 | });
85 |
86 | it("set functionality - thru lens, absent path", function () {
87 | var tx = menrva.transaction();
88 | d.set(tx, "something");
89 | tx.commit();
90 |
91 | chai.expect(a.value()).to.deep.equal({
92 | foo: {
93 | bar: 1,
94 | baz: 2
95 | },
96 | quux: "hello world",
97 | not: {
98 | here: "something",
99 | }
100 | });
101 |
102 | chai.expect(bCount).to.equal(1);
103 | chai.expect(cCount).to.equal(1);
104 | chai.expect(dCount).to.equal(2);
105 | chai.expect(eCount).to.equal(1);
106 | chai.expect(fCount).to.equal(1);
107 | });
108 |
109 | it("modify functionality - thru lens", function () {
110 | var tx = menrva.transaction();
111 | b.modify(tx, function (x) {
112 | return x * 3;
113 | });
114 | tx.commit();
115 |
116 | chai.expect(b.value()).to.equal(3);
117 | chai.expect(e.value()).to.deep.equal({ bar: 3, baz: 2});
118 | chai.expect(f.value()).to.equal(2);
119 |
120 | chai.expect(bCount).to.equal(2);
121 | chai.expect(cCount).to.equal(1);
122 | chai.expect(dCount).to.equal(1);
123 | chai.expect(eCount).to.equal(2);
124 | chai.expect(fCount).to.equal(1);
125 | });
126 |
127 | it("set functionality - thru lens, absent path", function () {
128 | var tx = menrva.transaction();
129 | d.modify(tx, function (x) {
130 | return x;
131 | });
132 | tx.commit();
133 |
134 | chai.expect(a.value()).to.deep.equal({
135 | foo: {
136 | bar: 1,
137 | baz: 2
138 | },
139 | quux: "hello world",
140 | not: {
141 | here: undefined, // not it exists, though undefined
142 | }
143 | });
144 |
145 | chai.expect(bCount).to.equal(1);
146 | chai.expect(cCount).to.equal(1);
147 | chai.expect(dCount).to.equal(1);
148 | chai.expect(eCount).to.equal(1);
149 | chai.expect(fCount).to.equal(1);
150 | });
151 | });
152 |
--------------------------------------------------------------------------------
/benchmark/diamond.js:
--------------------------------------------------------------------------------
1 | var Benchmark = require("benchmark");
2 | var menrva = require("../src/menrva.js");
3 | var Bacon = require("baconjs");
4 | var BaconModel = require("bacon.model");
5 | var Rx = require("rx");
6 |
7 | var quick = process.argv.indexOf("--quick") !== -1;
8 |
9 | var WIDTH = 3;
10 | var DEPTH = 5;
11 |
12 | var INITIAL_VALUE = 1;
13 | var SECOND_VALUE = 2;
14 |
15 | function inc(x) {
16 | return x + 1;
17 | }
18 |
19 | function plus() {
20 | var sum = 0;
21 | var len = arguments.length;
22 | for (var i = 0; i < len; i++) {
23 | sum += arguments[i];
24 | }
25 | return sum;
26 | }
27 |
28 | function diamond(src, width, depth, combinator) {
29 | if (depth === 0) {
30 | return src;
31 | } else {
32 | var branches = [];
33 | for (var i = 0; i < width; i++) {
34 | branches.push(diamond(src.map(inc), width, depth - 1, combinator));
35 | }
36 | return combinator(branches);
37 | }
38 | }
39 |
40 | // Bacon
41 | var baconBus = new Bacon.Bus();
42 | function baconCombine(array) {
43 | array = [plus].concat(array);
44 | return Bacon.combineWith.apply(undefined, array);
45 | }
46 | var baconDiamond = diamond(baconBus, WIDTH, DEPTH, baconCombine);
47 |
48 | // Bacon property
49 | var baconPropertyBus = new Bacon.Bus();
50 | var baconProperty = baconPropertyBus.toProperty(INITIAL_VALUE);
51 | var baconPropertyDiamond = diamond(baconProperty, WIDTH, DEPTH, baconCombine);
52 |
53 | // Bacon model
54 | var baconModel = new BaconModel.Model(INITIAL_VALUE);
55 | function baconModelCombine(array) {
56 | array = [plus].concat(array);
57 | return BaconModel.combineWith.apply(undefined, array);
58 | }
59 | var baconModelDiamond = diamond(baconModel, WIDTH, DEPTH, baconModelCombine);
60 |
61 | // Rx
62 | var rxSubject = new Rx.Subject();
63 | function rxCombine(array) {
64 | array = array.concat([plus]);
65 | return Rx.Observable.zip.apply(undefined, array);
66 | }
67 | var rxDiamond = diamond(rxSubject, WIDTH, DEPTH, rxCombine);
68 |
69 | // Menrva
70 | var menrvaSource = menrva.source(INITIAL_VALUE);
71 | function menrvaCombine(array) {
72 | array = array.concat([plus]);
73 | return menrva.combine.apply(undefined, array);
74 | }
75 | var menrvaDiamond = diamond(menrvaSource, WIDTH, DEPTH, menrvaCombine);
76 |
77 | // Verify correctness:
78 | var unsub, disposable;
79 |
80 | unsub = baconDiamond.onValue(function (x) {
81 | console.log("Bacon.js: ", x);
82 | });
83 | baconBus.push(INITIAL_VALUE);
84 | baconBus.push(SECOND_VALUE);
85 | unsub();
86 |
87 | unsub = baconPropertyDiamond.onValue(function (x) {
88 | console.log("Bacon.js Bus→Property:", x);
89 | });
90 | baconPropertyBus.push(SECOND_VALUE);
91 | unsub();
92 |
93 | unsub = baconModelDiamond.onValue(function (x) {
94 | console.log("Bacon.js Model: ", x);
95 | });
96 | baconModel.set(SECOND_VALUE);
97 | unsub();
98 |
99 | disposable = rxDiamond.subscribe(function (x) {
100 | console.log("Rx.JS Cold: ", x);
101 | });
102 | rxSubject.onNext(INITIAL_VALUE);
103 | rxSubject.onNext(SECOND_VALUE);
104 | disposable.dispose();
105 |
106 | unsub = menrvaDiamond.onValue(function (x) {
107 | console.log("menrva: ", x);
108 | });
109 | (function () {
110 | var tx = menrva.transaction();
111 | menrvaSource.set(tx, SECOND_VALUE);
112 | tx.commit();
113 | }());
114 | unsub();
115 |
116 | // Add callack
117 | baconDiamond.onValue(function (x) {});
118 | baconPropertyDiamond.onValue(function (x) {});
119 | baconModelDiamond.onValue(function (x) {});
120 | rxDiamond.subscribe(function (x) {});
121 | menrvaDiamond.onValue(function (x) {});
122 |
123 | // Options
124 | if (quick) {
125 | Benchmark.options.maxTime = 2;
126 | Benchmark.options.minSamples = 20;
127 | } else {
128 | Benchmark.options.maxTime = 10;
129 | Benchmark.options.minSamples = 200;
130 | }
131 |
132 | // Suite
133 | var suite = new Benchmark.Suite();
134 | suite.add("Bacon.js", function () {
135 | baconBus.push(Math.random());
136 | });
137 | suite.add("Bacon.js Bus→Property", function () {
138 | baconPropertyBus.push(Math.random());
139 | });
140 | suite.add("Bacon.js Model", function () {
141 | baconModel.set(Math.random());
142 | });
143 | suite.add("Rx.JS Cold", function () {
144 | rxSubject.onNext(Math.random());
145 | });
146 | suite.add("menrva", function (x) {
147 | var tx = menrva.transaction();
148 | menrvaSource.set(tx, Math.random());
149 | tx.commit();
150 | });
151 |
152 | // Run suite;
153 | suite
154 | .on("cycle", function(event) {
155 | console.log(String(event.target));
156 | })
157 | .on("complete", function() {
158 | console.log("Fastest is " + this.filter("fastest").pluck("name"));
159 | })
160 | // run async
161 | .run();
162 |
--------------------------------------------------------------------------------
/docs/20140703-freaklies/figures/data-events.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
49 |
--------------------------------------------------------------------------------
/src/signal.js:
--------------------------------------------------------------------------------
1 | /*
2 | * menrva
3 | * https://github.com/phadej/menrva
4 | *
5 | * Copyright (c) 2014 Oleg Grenrus
6 | * Licensed under the MIT license.
7 | */
8 |
9 | "use strict";
10 |
11 | var egal = require("./egal.js");
12 | var util = require("./util.js");
13 |
14 | /**
15 | ### Signal
16 |
17 | The core type of menrva. `Signal` is abstract class, and cannot be created explicitly.
18 |
19 | Similar concepts are: *Behaviours* in FRP, *Properties* in bacon.js.
20 |
21 | You can add methods to `Signal`'s prototype. They will be available on all signals.
22 | */
23 | function Signal() {}
24 |
25 | // Each signal has an unique index.
26 | var index = 0;
27 |
28 | function initSignal(signal, value, eq) {
29 | signal.children = [];
30 | signal.callbacks = [];
31 | signal.v = value;
32 |
33 | // `index` is used to implement faster sets of signals
34 | signal.index = index++;
35 |
36 | // `rank` is used to sort signals topologically
37 | signal.rank = signal.calculateRank();
38 |
39 | // `eq` is an equality decision function on the signal values
40 | signal.eq = eq || egal;
41 | }
42 |
43 | function CombinedSignal(parents, f, eq) {
44 | this.parents = parents;
45 | this.f = f;
46 | var value = this.calculate();
47 | initSignal(this, value, eq);
48 | }
49 |
50 | CombinedSignal.prototype = new Signal();
51 |
52 | // rank of combined signal is 1 + maximum rank of parents
53 | CombinedSignal.prototype.calculateRank = function () {
54 | return Math.max.apply(Math, util.pluck(this.parents, "rank")) + 1;
55 | };
56 |
57 | CombinedSignal.prototype.calculate = function () {
58 | return this.f.call(undefined, util.pluck(this.parents, "v"));
59 | };
60 |
61 | /**
62 | #### signal.map
63 |
64 | > map (@ : Signal a, f : a -> b, eq = egal : b -> b -> boolean) : Signal b
65 | */
66 | Signal.prototype.map = function(f, eq) {
67 | var mapped = new CombinedSignal([this], function (xs) {
68 | return f(xs[0]);
69 | }, eq);
70 | this.children.push(mapped);
71 | return mapped;
72 | };
73 |
74 | /**
75 | #### signal.onValue
76 |
77 | > onValue (@ : Signal a, callback : a -> void) -> Unsubscriber
78 |
79 | Add value callback. `callback` is immediately executed with the current value of signal.
80 | After than `callback` will be called, each time signal's value changes.
81 |
82 | The return value is a function, which will remove the callback if executed.
83 | */
84 | Signal.prototype.onValue = function (callback) {
85 | // we wrap callback in function, to make it unique
86 | var wrapped = function (x) { callback(x); };
87 |
88 | // add to callbacks list
89 | this.callbacks.push(wrapped);
90 |
91 | // execute the callback *synchronously*
92 | callback(this.v);
93 |
94 | // return unsubscriber
95 | var that = this;
96 | return function () {
97 | var index = that.callbacks.indexOf(wrapped);
98 | if (index !== -1) {
99 | that.callbacks.splice(index, 1);
100 | }
101 | };
102 | };
103 |
104 | /**
105 | #### signal.value()
106 |
107 | > value (@ : Signal a): Signal a
108 |
109 | Returns the current value of signal.
110 | */
111 | Signal.prototype.value = function() {
112 | return this.v;
113 | };
114 |
115 | /**
116 | ### Source
117 |
118 | A signal which value you can set.
119 |
120 | Similar concepts are: *Bacon.Model* in bacon.js, *BehaviourSubject* in Rx.
121 |
122 |
123 | #### source
124 |
125 | > source (initialValue : a, eq = egal : a -> a -> boolean) : Source a
126 | */
127 | function Source(initialValue, eq) {
128 | initSignal(this, initialValue, eq);
129 | }
130 |
131 | function source(initialValue, eq) {
132 | return new Source(initialValue, eq);
133 | }
134 |
135 | Source.prototype = new Signal();
136 |
137 | Source.prototype.calculateRank = function () {
138 | return 0;
139 | };
140 |
141 | /**
142 | #### source.set
143 |
144 | > set (@ : Source a, tx : Transaction, value : a) : void
145 | */
146 | Source.prototype.set = function (transaction, value) {
147 | transaction.addAction({
148 | type: "set",
149 | signal: this,
150 | value: value,
151 | });
152 | };
153 |
154 | /**
155 | #### source.modify
156 |
157 | > modify (@ : Source a, tx : Transaction, f : a -> a) : void
158 |
159 | Mofify source value. `f` will be called with current value of signal inside the transaction.
160 | */
161 | Source.prototype.modify = function (transaction, f) {
162 | transaction.addAction({
163 | type: "modify",
164 | signal: this,
165 | f: f,
166 | });
167 | };
168 |
169 |
170 | /**
171 | ### Signal combinators
172 | */
173 |
174 | /**
175 | #### combine
176 |
177 | > combine (Signal a..., f : a... -> b) : Signal b
178 |
179 | Applicative n-ary lift. Lift pure function to operate on signals:
180 | ```js
181 | var $sum = menrva.combine($a, $b, function (a, b) {
182 | return a + b;
183 | });
184 | ```
185 | */
186 | function combine() {
187 | var signals = Array.prototype.slice.call(arguments, 0, -1);
188 | var f = arguments[arguments.length - 1];
189 |
190 | var mapped = new CombinedSignal(signals, function (values) {
191 | return f.apply(undefined, values);
192 | });
193 |
194 | // connect to parent
195 | signals.forEach(function (parent) {
196 | parent.children.push(mapped);
197 | });
198 |
199 | return mapped;
200 | }
201 |
202 | module.exports = {
203 | Signal: Signal,
204 | Source: Source,
205 | source: source,
206 | combine: combine,
207 | initSignal: initSignal,
208 | // Internal:
209 | CombinedSignal: CombinedSignal,
210 | };
211 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # menrva
2 |
3 |
4 |
5 | [](http://travis-ci.org/phadej/menrva)
6 | [](https://www.npmjs.org/package/menrva)
7 | [](https://david-dm.org/phadej/menrva)
8 | [](https://david-dm.org/phadej/menrva#info=devDependencies)
9 | [](https://coveralls.io/r/phadej/menrva?branch=master)
10 | [](https://codeclimate.com/github/phadej/menrva)
11 |
12 | Ambitious data-flow library.
13 |
14 | ## Getting Started
15 | Install the module with: `npm install menrva`
16 |
17 | ```js
18 | var menrva = require('menrva');
19 | menrva.some('awe'); // some, as in awesome?
20 | ```
21 |
22 | ## API
23 |
24 |
25 | ### Signal
26 |
27 | The core type of menrva. `Signal` is abstract class, and cannot be created explicitly.
28 |
29 | Similar concepts are: *Behaviours* in FRP, *Properties* in bacon.js.
30 |
31 | You can add methods to `Signal`'s prototype. They will be available on all signals.
32 |
33 |
34 | #### signal.map
35 |
36 | > map (@ : Signal a, f : a -> b, eq = egal : b -> b -> boolean) : Signal b
37 |
38 |
39 | #### signal.onValue
40 |
41 | > onValue (@ : Signal a, callback : a -> void) -> Unsubscriber
42 |
43 | Add value callback. `callback` is immediately executed with the current value of signal.
44 | After than `callback` will be called, each time signal's value changes.
45 |
46 | The return value is a function, which will remove the callback if executed.
47 |
48 |
49 | #### signal.value()
50 |
51 | > value (@ : Signal a): Signal a
52 |
53 | Returns the current value of signal.
54 |
55 |
56 | ### Source
57 |
58 | A signal which value you can set.
59 |
60 | Similar concepts are: *Bacon.Model* in bacon.js, *BehaviourSubject* in Rx.
61 |
62 |
63 | #### source
64 |
65 | > source (initialValue : a, eq = egal : a -> a -> boolean) : Source a
66 |
67 |
68 | #### source.set
69 |
70 | > set (@ : Source a, tx : Transaction, value : a) : void
71 |
72 |
73 | #### source.modify
74 |
75 | > modify (@ : Source a, tx : Transaction, f : a -> a) : void
76 |
77 | Mofify source value. `f` will be called with current value of signal inside the transaction.
78 |
79 |
80 | ### Signal combinators
81 |
82 |
83 | #### combine
84 |
85 | > combine (Signal a..., f : a... -> b) : Signal b
86 |
87 | Applicative n-ary lift. Lift pure function to operate on signals:
88 | ```js
89 | var $sum = menrva.combine($a, $b, function (a, b) {
90 | return a + b;
91 | });
92 | ```
93 |
94 |
95 |
96 | ### Transaction
97 |
98 | One gathers atomic updates into single transaction (to avoid glitches).
99 |
100 | ```js
101 | var tx = menrva.transaction();
102 | sourceA.set(tx, 42);
103 | sourceB.modify(tx, function (x) { return x + x; });
104 | tx.commit(); // not necessary, transactions are auto-commited
105 | ```
106 |
107 | There are also optional syntaxes for simple transactions:
108 | ```js
109 | menrva.transaction()
110 | .set(sourceA, 42)
111 | .modify(sourceB, function (x) { return x + x; })
112 | .commit();
113 | ```
114 | or even
115 | ```js
116 | menrva.transaction([sourceA, 42, sourceB, function(x) { return x + x; }]).commit();
117 | ```
118 |
119 |
120 | #### transaction
121 |
122 | > transaction (facts) : Transaction
123 |
124 | Create transaction.
125 |
126 | Shorthand syntax:
127 |
128 | > transaction ([sourceA, valueA, sourceB, valueB ...]) : Transaction
129 |
130 | If `value` is function, `source.modify(tx, value)` is called; otherwise `source.set(tx, value)`.
131 |
132 |
133 | #### transaction.commit
134 |
135 | Commit the transaction, forcing synchronous data propagation.
136 |
137 |
138 | #### transaction.rollback
139 |
140 | Rollback the transaction. Maybe be called multiple times (consecutives calls are no-op).
141 |
142 | *Note: for now `rollback` only resets the pending actions in transactions. Transaction is still valid, and more actions can be added*
143 |
144 |
145 |
146 | ### Lens
147 |
148 | Lenses are composable functional references.
149 | They allow you to *access* and *modify* data potentially very deep within a structure!
150 |
151 |
152 | #### source.zoom
153 |
154 | > zoom (@ : Source a, path : Path a b, eq = egal : b -> b -> boolean) : Source b
155 |
156 | Zoom (or focus) into part specified by `path` of the original signal.
157 | One can `set` and `modify` zoomed signals, they act as sources.
158 |
159 | ```js
160 | var quux = source.zoom("foo.bar.quux");
161 | ```
162 |
163 |
164 |
165 | ### Convenience methods
166 |
167 | #### signal.log
168 |
169 | > signal.log (@ : Signal a, args...) : Unsubscriber
170 |
171 | Essentially `signal.onValue(console.log.bind(console, args...))
172 |
173 |
174 | #### signal.onSpread
175 |
176 | > signal.onSpread (@ : Signal [a, b...], callback : a -> b ... -> void) : Unsubscriber
177 |
178 | `onValue` with signal's tuple arguments spread.
179 |
180 |
181 | #### tuple
182 |
183 | > tuple (x : Signal a, y : Signal b...) : Signal [a, b...]
184 |
185 | Combine signals into tuple.
186 |
187 |
188 | #### sequence
189 |
190 | > sequence [Signal a, Signal b, ..] : Signal [a, b...]
191 |
192 | In promise libraries this might be called `all`.
193 |
194 |
195 | #### record
196 |
197 | > record {k: Signal a, l: Signal b...} : Signal {k: a, l: b...}
198 |
199 | Like `sequence` but for records i.e. objects.
200 |
201 |
202 |
203 | ### Equalities
204 |
205 | #### egal
206 |
207 | > egal (a, b) : boolean
208 |
209 | Identity check. `Object.is`. http://wiki.ecmascript.org/doku.php?id=harmony:egal
210 |
211 |
212 |
213 | ### Option
214 |
215 | Also known as `Maybe`.
216 |
217 |
218 | #### option.equals
219 |
220 | > equals (@ : option a, other : *, eq = eqal : a -> a -> boolean) : boolean
221 |
222 | Equality check.
223 |
224 |
225 | #### option.map
226 |
227 | > map (@ : option a, f : a -> b) : option b
228 |
229 |
230 | #### option.elim
231 |
232 | > elim (@ : option a, x : b, f : a -> b) : b
233 |
234 |
235 |
236 | #### option.orElse
237 |
238 | > orElse (@ : option a, x : a) : a
239 |
240 |
241 |
242 | ## Contributing
243 |
244 |
245 | In lieu of a formal styleguide, take care to maintain the existing coding style.
246 |
247 | Add tests for any new or changed functionality. 100% coverage isn't hard. Semantic coverage is important too though.
248 |
249 | Note: `README.md` is autogenerated file.
250 |
251 | ## Release History
252 |
253 |
254 | - **0.0.7** `sequence` and `record`
255 | - **0.0.6** Convenience methods
256 | - **0.0.5** Lens
257 | - **0.0.4** Internal improvements
258 | - **0.0.3** Slight changes in API
259 | - **0.0.2** Basic data-flow functionality
260 | - **0.0.1** Name reservation
261 |
262 | ## License
263 |
264 | Copyright (c) 2014 Oleg Grenrus.
265 | Licensed under the MIT license.
266 |
--------------------------------------------------------------------------------
/src/transaction.js:
--------------------------------------------------------------------------------
1 | /*
2 | * menrva
3 | * https://github.com/phadej/menrva
4 | *
5 | * Copyright (c) 2014 Oleg Grenrus
6 | * Licensed under the MIT license.
7 | */
8 |
9 | "use strict";
10 |
11 | var util = require("./util.js");
12 |
13 | /**
14 | ### Transaction
15 |
16 | One gathers atomic updates into single transaction (to avoid glitches).
17 |
18 | ```js
19 | var tx = menrva.transaction();
20 | sourceA.set(tx, 42);
21 | sourceB.modify(tx, function (x) { return x + x; });
22 | tx.commit(); // not necessary, transactions are auto-commited
23 | ```
24 |
25 | There are also optional syntaxes for simple transactions:
26 | ```js
27 | menrva.transaction()
28 | .set(sourceA, 42)
29 | .modify(sourceB, function (x) { return x + x; })
30 | .commit();
31 | ```
32 | or even
33 | ```js
34 | menrva.transaction([sourceA, 42, sourceB, function(x) { return x + x; }]).commit();
35 | ```
36 | */
37 | function Transaction() {
38 | this.actions = [];
39 | this.commitScheduled = false;
40 | }
41 |
42 | /**
43 | #### transaction
44 |
45 | > transaction (facts) : Transaction
46 |
47 | Create transaction.
48 |
49 | Shorthand syntax:
50 |
51 | > transaction ([sourceA, valueA, sourceB, valueB ...]) : Transaction
52 |
53 | If `value` is function, `source.modify(tx, value)` is called; otherwise `source.set(tx, value)`.
54 | */
55 | function transaction(facts) {
56 | var tx = new Transaction();
57 |
58 | if (Array.isArray(facts)) {
59 | var len = facts.length;
60 | for (var i = 0; i < len; i += 2) {
61 | var source = facts[i];
62 | var value = facts[i + 1];
63 | if (typeof value === "function") {
64 | source.modify(tx, value);
65 | } else {
66 | source.set(tx, value);
67 | }
68 | }
69 | }
70 |
71 | return tx;
72 | }
73 |
74 | /**
75 | #### transaction.commit
76 |
77 | Commit the transaction, forcing synchronous data propagation.
78 | */
79 |
80 | function calculateUpdates(actions) {
81 | var updates = {};
82 | var len = actions.length;
83 | for (var i = 0; i < len; i++) {
84 | var action = actions[i];
85 | // find update fact for signal
86 | var update = updates[action.signal.index];
87 |
88 | // if not update found, create new for action's signal
89 | if (!update) {
90 | update = {
91 | signal: action.signal,
92 | value: action.signal.v,
93 | };
94 | updates[action.signal.index] = update;
95 | }
96 |
97 | // perform action
98 | switch (action.type) {
99 | case "set":
100 | update.value = action.value;
101 | break;
102 | case "modify":
103 | update.value = action.f(update.value);
104 | break;
105 | }
106 | }
107 |
108 | return util.values(updates);
109 | }
110 |
111 | function initialSet(updates) {
112 | var updated = [];
113 | var len = updates.length;
114 | for (var i = 0; i < len; i++) {
115 | var update = updates[i];
116 | // if different value
117 | if (!update.signal.eq(update.signal.v, update.value)) {
118 | // set it
119 | update.signal.v = update.value;
120 |
121 | // collect updated source signal
122 | updated.push(update.signal);
123 | }
124 | }
125 | return updated;
126 | }
127 |
128 | function triggerOnValue(updated) {
129 | var len = updated.length;
130 | for (var i = 0; i < len; i++) {
131 | var updatedSignal = updated[i];
132 | var value = updatedSignal.v;
133 | var callbacks = updatedSignal.callbacks;
134 | var callbacksLen = callbacks.length;
135 | for (var j = 0; j < callbacksLen; j++) {
136 | callbacks[j](value);
137 | }
138 | }
139 | }
140 |
141 | Transaction.prototype.commit = function () {
142 |
143 | // clear timeout
144 | if (this.commitScheduled) {
145 | clearTimeout(this.commitScheduled);
146 | this.commitScheduled = false;
147 | }
148 |
149 | // If nothing to do, short circuit
150 | if (this.actions.length === 0) {
151 | return;
152 | }
153 |
154 | // Data flow
155 |
156 | // traverse actions to aquire new values
157 | var updates = calculateUpdates(this.actions);
158 |
159 | // Apply updates, and collect updated signals
160 | var updated = initialSet(updates);
161 |
162 | // seed propagation push-pull propagation with children of updated sources
163 | var signals = {};
164 | updated.forEach(function (update) {
165 | update.children.forEach(function (child) {
166 | signals[child.index] = child;
167 | });
168 | });
169 |
170 | // until there aren't any signals
171 | while (!util.objIsEmpty(signals)) {
172 | // minimum rank
173 | var rank = Infinity;
174 | for (var rankK in signals) {
175 | rank = Math.min(rank, signals[rankK].rank);
176 | }
177 |
178 | var next = [];
179 | var curr = [];
180 |
181 | for (var k in signals) {
182 | var signal = signals[k];
183 | // skip signals of different (larger!) rank
184 | if (signal.rank !== rank) {
185 | continue;
186 | }
187 |
188 | // new value
189 | var value = signal.calculate();
190 |
191 | // if value is changed
192 | if (!signal.eq(signal.v, value)) {
193 | // set the value
194 | signal.v = value;
195 |
196 | // add signal to updated list
197 | updated.push(signal);
198 |
199 | // add children of updated signal to list of traversable signals
200 | var childrenlen = signal.children.length;
201 | for (var childIdx = 0; childIdx < childrenlen; childIdx++) {
202 | var child = signal.children[childIdx];
203 | next.push(child);
204 | }
205 | }
206 |
207 | // we are done with this signal
208 | curr.push(signal.index);
209 | }
210 |
211 | // Remove traversed
212 | var currLen = curr.length;
213 | for (var currIdx = 0; currIdx < currLen; currIdx++) {
214 | delete signals[curr[currIdx]];
215 | }
216 |
217 | // add next
218 | var nextLen = next.length;
219 | for (var nextIdx = 0; nextIdx < nextLen; nextIdx++) {
220 | signals[next[nextIdx].index] = next[nextIdx];
221 | }
222 | }
223 |
224 | // Trigger onValue callbacks
225 | triggerOnValue(updated);
226 |
227 | // rest cleanupg
228 | this.actions = [];
229 | };
230 |
231 | /**
232 | #### transaction.rollback
233 |
234 | Rollback the transaction. Maybe be called multiple times (consecutives calls are no-op).
235 |
236 | *Note: for now `rollback` only resets the pending actions in transactions. Transaction is still valid, and more actions can be added*
237 | */
238 | Transaction.prototype.rollback = function() {
239 | if (this.commitScheduled) {
240 | clearTimeout(this.commitScheduled);
241 | }
242 | this.actions = [];
243 | this.commitScheduled = false;
244 | };
245 |
246 | Transaction.prototype.deferCommit = function () {
247 | if (!this.commitScheduled) {
248 | var tx = this;
249 | this.commitScheduled = setTimeout(function () {
250 | tx.commit();
251 | });
252 | }
253 | };
254 |
255 | Transaction.prototype.addAction = function (action) {
256 | this.actions.push(action);
257 | this.deferCommit();
258 | };
259 |
260 | Transaction.prototype.set = function (source, value) {
261 | source.set(this, value);
262 | return this;
263 | };
264 |
265 | Transaction.prototype.modify = function (source, f) {
266 | source.modify(this, f);
267 | return this;
268 | };
269 |
270 | module.exports = transaction;
271 |
--------------------------------------------------------------------------------
/test/transaction-test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var menrva = require("../src/menrva.js");
4 | var assert = require("assert");
5 | var _ = require("lodash");
6 | var chai = require("chai");
7 |
8 | describe("transaction", function () {
9 | /*
10 | a = source()
11 | b = a + 1
12 | c = a + b = 2 * a + 1
13 | */
14 | var a, b, c;
15 | beforeEach(function () {
16 | a = menrva.source(1);
17 | b = a.map(function (x) { return x + 1; });
18 | c = menrva.combine(a, b, function (x, y) {
19 | return x + y;
20 | });
21 | });
22 |
23 | describe("multiple sets", function () {
24 | it("last one wins", function () {
25 | var count = 0;
26 | c.onValue(function (y) {
27 | count += 1;
28 | });
29 |
30 | chai.expect(c.value()).to.equal(3);
31 | chai.expect(count).to.equal(1);
32 |
33 | var tx = menrva.transaction();
34 | a.set(tx, 2);
35 | a.set(tx, 3);
36 | tx.commit();
37 |
38 | chai.expect(c.value()).to.equal(7);
39 | chai.expect(count).to.equal(2);
40 | });
41 |
42 | it("last one wins, equality", function () {
43 | var count = 0;
44 | c.onValue(function (y) {
45 | count += 1;
46 | });
47 |
48 | chai.expect(c.value()).to.equal(3);
49 | chai.expect(count).to.equal(1);
50 |
51 | var tx = menrva.transaction();
52 | a.set(tx, 2);
53 | a.set(tx, 1);
54 | tx.commit();
55 |
56 | chai.expect(c.value()).to.equal(3);
57 | chai.expect(count).to.equal(1);
58 | });
59 | });
60 |
61 | describe("rollback", function () {
62 | it("resets the transaction", function () {
63 | var count = 0;
64 | c.onValue(function (y) {
65 | count += 1;
66 | });
67 |
68 | chai.expect(c.value()).to.equal(3);
69 | chai.expect(count).to.equal(1);
70 |
71 | var tx = menrva.transaction();
72 | a.set(tx, 2);
73 | tx.rollback();
74 |
75 | // TODO: we can make this either fail for rollbacked transactions, or let set's do nothing
76 | tx.commit();
77 |
78 | chai.expect(c.value()).to.equal(3);
79 | chai.expect(count).to.equal(1);
80 | });
81 |
82 | it("can be called multiple times", function () {
83 | var count = 0;
84 | c.onValue(function (y) {
85 | count += 1;
86 | });
87 |
88 | chai.expect(c.value()).to.equal(3);
89 | chai.expect(count).to.equal(1);
90 |
91 | var tx = menrva.transaction();
92 | a.set(tx, 2);
93 | tx.rollback();
94 | tx.rollback();
95 |
96 | // TODO: we can make this either fail for rollbacked transactions, or let set's do nothing
97 | tx.commit();
98 |
99 | chai.expect(c.value()).to.equal(3);
100 | chai.expect(count).to.equal(1);
101 | });
102 | });
103 |
104 | it("will eventually auto-commit", function (done) {
105 | var count = 0;
106 | c.onValue(function (y) {
107 | count += 1;
108 | });
109 |
110 | chai.expect(c.value()).to.equal(3);
111 | chai.expect(count).to.equal(1);
112 |
113 | var tx = menrva.transaction();
114 | a.set(tx, 2);
115 |
116 | setTimeout(function () {
117 | chai.expect(c.value()).to.equal(5);
118 | chai.expect(count).to.equal(2);
119 | done();
120 | }, 10)
121 | });
122 | });
123 |
124 | describe("transaction - a + b", function () {
125 | /*
126 | a = source()
127 | b = source()
128 | c = a + b
129 | */
130 | var a, b, c;
131 | beforeEach(function () {
132 | a = menrva.source(1);
133 | b = menrva.source(2);
134 | c = menrva.combine(a, b, function (x, y) {
135 | return x + y;
136 | });
137 | });
138 |
139 | describe("multiple sets", function () {
140 | it("inside single transaction", function () {
141 | var count = 0;
142 | c.onValue(function (y) {
143 | count += 1;
144 | });
145 |
146 | chai.expect(c.value()).to.equal(3);
147 | chai.expect(count).to.equal(1);
148 |
149 | var tx = menrva.transaction();
150 | a.set(tx, 2);
151 | b.set(tx, 3);
152 | tx.commit();
153 |
154 | chai.expect(c.value()).to.equal(5);
155 | chai.expect(count).to.equal(2);
156 | });
157 |
158 | it("skipping duplicates", function () {
159 | var count = 0;
160 | c.onValue(function (y) {
161 | count += 1;
162 | });
163 |
164 | chai.expect(c.value()).to.equal(3);
165 | chai.expect(count).to.equal(1);
166 |
167 | var tx = menrva.transaction();
168 | a.set(tx, 2);
169 | b.set(tx, 1);
170 | tx.commit();
171 |
172 | chai.expect(c.value()).to.equal(3);
173 | chai.expect(count).to.equal(1);
174 | });
175 | });
176 | });
177 |
178 | describe("transaction - a + b + c", function () {
179 | /*
180 | a = source()
181 | b = source()
182 | c = source();
183 | d = a + b
184 | e = d + c = a + b + c
185 | */
186 | var a, b, c, d, e;
187 | beforeEach(function () {
188 | a = menrva.source(1);
189 | b = menrva.source(2);
190 | c = menrva.source(3);
191 | d = menrva.combine(a, b, function (x, y) {
192 | return x + y;
193 | });
194 | e = menrva.combine(d, c, function (x, y) {
195 | return x + y;
196 | });
197 | });
198 |
199 | describe("multiple sets", function () {
200 | it("inside single transaction", function () {
201 | var count = 0;
202 | e.onValue(function (y) {
203 | count += 1;
204 | });
205 |
206 | chai.expect(e.value()).to.equal(6);
207 | chai.expect(count).to.equal(1);
208 |
209 | var tx = menrva.transaction();
210 | a.set(tx, 2);
211 | b.set(tx, 3);
212 | tx.commit();
213 |
214 | chai.expect(e.value()).to.equal(8);
215 | chai.expect(count).to.equal(2);
216 | });
217 |
218 | it("skipping duplicates", function () {
219 | var countD = 0;
220 | d.onValue(function (y) {
221 | countD += 1;
222 | });
223 |
224 | var countE = 0;
225 | e.onValue(function (y) {
226 | countE += 1;
227 | });
228 |
229 | chai.expect(d.value()).to.equal(3);
230 | chai.expect(e.value()).to.equal(6);
231 | chai.expect(countD).to.equal(1);
232 | chai.expect(countE).to.equal(1);
233 |
234 | var tx = menrva.transaction();
235 | a.set(tx, 2);
236 | b.set(tx, 3);
237 | c.set(tx, 1);
238 | tx.commit();
239 |
240 | chai.expect(d.value()).to.equal(5);
241 | chai.expect(e.value()).to.equal(6);
242 | chai.expect(countD).to.equal(2);
243 | chai.expect(countE).to.equal(1);
244 | });
245 | });
246 |
247 | describe("syntax", function () {
248 | var a, b;
249 |
250 | function double(x) {
251 | return x + x;
252 | }
253 |
254 | beforeEach(function () {
255 | a = menrva.source(1);
256 | b = menrva.source(2);
257 | });
258 |
259 | it("imperative", function () {
260 | chai.expect(a.value()).to.equal(1);
261 | chai.expect(b.value()).to.equal(2);
262 |
263 | var tx = menrva.transaction();
264 | a.set(tx, 42);
265 | b.modify(tx, double);
266 | tx.commit();
267 |
268 | chai.expect(a.value()).to.equal(42);
269 | chai.expect(b.value()).to.equal(4);
270 | });
271 |
272 | it("chain", function () {
273 | chai.expect(a.value()).to.equal(1);
274 | chai.expect(b.value()).to.equal(2);
275 |
276 | menrva.transaction()
277 | .set(a, 42)
278 | .modify(b, double)
279 | .commit();
280 |
281 | chai.expect(a.value()).to.equal(42);
282 | chai.expect(b.value()).to.equal(4);
283 | });
284 |
285 | it("short", function () {
286 | chai.expect(a.value()).to.equal(1);
287 | chai.expect(b.value()).to.equal(2);
288 |
289 | menrva.transaction([a, 42, b, double]).commit();
290 |
291 | chai.expect(a.value()).to.equal(42);
292 | chai.expect(b.value()).to.equal(4);
293 | });
294 | });
295 | });
296 |
--------------------------------------------------------------------------------
/docs/20140703-freaklies/freaklies.tex:
--------------------------------------------------------------------------------
1 | \documentclass[10pt,fleqn]{article}
2 | \usepackage[utf8]{inputenc}
3 | \usepackage[T1]{fontenc}
4 | \usepackage{fullpage}
5 | \usepackage{amsmath,amssymb,amsthm,mathtools}
6 | \usepackage{mathrsfs} % mathscr
7 | \usepackage{color}
8 | \usepackage[paperwidth=160mm,paperheight=90mm,margin=10mm]{geometry}
9 |
10 | % multicol
11 | \usepackage{multicol}
12 | \usepackage{array}
13 |
14 | % graphics
15 | \usepackage{graphicx}
16 | \DeclareGraphicsRule{*}{mps}{*}{}
17 |
18 | \usepackage{hyperref}
19 | \hypersetup{
20 | colorlinks=true
21 | }
22 |
23 | \usepackage{mathpazo}
24 | \usepackage{helvet}
25 | %\usepackage{courier}
26 | \usepackage{inconsolata}
27 |
28 | \usepackage{titling}
29 | \newcommand{\subtitle}[1]{%
30 | \posttitle{%
31 | \par\end{center}
32 | \begin{center}\large#1\end{center}
33 | \vskip0.5em}%
34 | }
35 |
36 | \title{$\mathbb{MENRVA}$}
37 | \subtitle{@futurice freaklies}
38 | \author{Oleg Grenrus}
39 | \date{July 03, 2014}
40 |
41 | \DeclareMathOperator*{\inr}{\mathsf{inr}}
42 | \DeclareMathOperator*{\inl}{\mathsf{inl}}
43 | \DeclareMathOperator*{\none}{\mathsf{none}}
44 | \DeclareMathOperator*{\some}{\mathsf{some}}
45 | \DeclareMathOperator*{\ap}{\mathsf{ap}}
46 | \DeclareMathOperator*{\fst}{\mathsf{fst}}
47 | \DeclareMathOperator*{\snd}{\mathsf{snd}}
48 |
49 | % Theorems
50 | % http://en.wikibooks.org/wiki/LaTeX/Theorems
51 |
52 | \theoremstyle{definition}
53 | \newtheorem{definition}{Definition}[section]
54 | \newtheorem{example}[definition]{Example}
55 | \newtheorem{exercise}[definition]{Exercise}
56 | \newtheorem{note}[definition]{Note}
57 |
58 | \theoremstyle{plain}
59 | \newtheorem{proposition}[definition]{Proposition}
60 | \newtheorem{lemma}[definition]{Lemma}
61 | \newtheorem{theorem}[definition]{Theorem}
62 |
63 | \pagestyle{empty}
64 |
65 | \definecolor{blue}{rgb}{0,0,0.5}
66 | \definecolor{green}{rgb}{0,0.5,0}
67 | \definecolor{red}{rgb}{0.5,0,0}
68 | \definecolor{orange}{rgb}{1,0.5,0}
69 | \definecolor{darkorange}{rgb}{0.5,0.25,0}
70 | \definecolor{lightgrey}{rgb}{0.7,0.7,0.7}
71 | \definecolor{grey}{rgb}{0.5,0.5,0.5}
72 |
73 | \setlength\parindent{0pt}
74 | \setlength{\parskip}{3mm plus1mm minus1mm}
75 |
76 | \newcommand{\identifier}[1]{\texttt{\color{blue}#1}}
77 | \newcommand{\constructor}[1]{\ensuremath{\mathsf{\color{green}#1}}}
78 | \newcommand{\functor}[1]{\ensuremath{\mathcal{\color{darkorange}#1}}}
79 |
80 | \begin{document}
81 |
82 |
83 | \maketitle
84 | \thispagestyle{empty}
85 | \newpage
86 |
87 | {\LARGE$\mathbb{MENRVA}$-- yet another reactive library}
88 |
89 | \href{https://github.com/baconjs/bacon.js}{\textsc{Bacon.js}} is (almost) non-glitchy, but not-so performant.
90 | \href{https://github.com/Reactive-Extensions/RxJS}{\textsc{RxJS}} is glitchy but performant.
91 |
92 | I begin to bias towards using cold-observables of \textsc{RxJS}\footnote{Can use \textsc{Bacon.js} too, but I don't need glitch-free events} (the good parts?) with data-flow propagation of something else ($=$ \textsc{Menrva}).
93 |
94 | \begin{center}
95 | \includegraphics[scale=0.7]{figures/data-events.mps}
96 | \end{center}
97 |
98 | \newpage
99 |
100 | \section*{Semantics}
101 |
102 | Following definitions are from \emph{Push-pull functional reactive programming}\footnote{Conal Elliott, \emph{Push-pull functional reactive programming}, Haskell Symposium, 2009, \url{http://conal.net/papers/push-pull-frp/}}.
103 |
104 | \subsection*{FRP -- Behavior}
105 |
106 | Semantically, a \emph{(reactive) behavior} is just a function of time. In \textsc{Menrva} we call them \emph{signals}.
107 | \begin{align*}
108 | \mathcal{S}\,\alpha = \mathcal{T} \to \alpha
109 | \end{align*}
110 |
111 | Historically in FRP, $\mathcal{T} = \mathbb{R}$. As we’ll see, however, the semantics of behaviors assumes only that $\mathcal{T}$ is totally ordered.
112 |
113 | \newpage
114 |
115 | \subsubsection*{Applicative functor}
116 |
117 | Pure functions can be "lifted" to apply to signals. Classic FRP (CFRP) had a family of lifting combinators:
118 | \begin{align*}
119 | \texttt{lift}_n &:: (\alpha_1 \to \cdots \alpha_n \to \beta) \to (\mathcal{S}\,\alpha_1 \to \cdots \mathcal{S}\,\alpha_n \to \mathcal{S}\,\beta)
120 | \end{align*}
121 | Lifting is pointwise and synchronous:
122 | \begin{align*}
123 | \texttt{lift}_n\,f\,b_1\cdots b_n &= \lambda\,t \mapsto f (b_1\, t)\cdots (b_n\, t)
124 | \end{align*}
125 | The \emph{Functor} \texttt{map} is $\texttt{lift}_1$:
126 | \begin{align*}
127 | \texttt{map}\,f\,b = f \circ b
128 | \end{align*}
129 |
130 | \newpage
131 |
132 | In \textsc{Menrva} \texttt{map} is \texttt{map} and $\texttt{lift}_n$ is \texttt{combine} (with function parameter at the end):
133 | \begin{verbatim}
134 | var $sq = $source.map(function (x) {
135 | return x * x;
136 | });
137 |
138 | var $quad = menrva.combine($a, $b, $c, function (a, b, c) {
139 | return a * b + c;
140 | });
141 | \end{verbatim}
142 |
143 | And that's the whole (functional) API.
144 |
145 | {\small There is \texttt{onValue} to observe changes. And \texttt{set} and \texttt{modify} to initiate changes.}
146 |
147 | \newpage
148 |
149 | \subsubsection*{Monad}
150 |
151 | Although Signal is a semantic \emph{Monad} as well, \textsc{Menrva} doesn't have monadic \texttt{join} combinator.
152 |
153 | Semantic of \texttt{join} is simple, but the correct implementation hard.
154 | \begin{align*}
155 | \texttt{join}\,s = t \mapsto s\,t\,t
156 | \end{align*}
157 |
158 | Monad instance would make possible to make dynamic data flow networks. With applicative functor the shape of the network is static (and you can't make loops) $\equiv$ simple and easy.
159 |
160 | \newpage
161 |
162 | \section*{Examples}
163 |
164 | \url{https://github.com/phadej/menrva/tree/master/examples}
165 |
166 | \begin{itemize}
167 | \item \emph{counter} examples (pun not-intended). With data-first approach, you segregate \emph{query} from \emph{control}.
168 | \item \emph{suggestions} \href{https://gist.github.com/staltz/868e7e9bc2a7b8c1f754}{\emph{The introduction to Reactive Programming you've been missing}}
169 | \end{itemize}
170 |
171 | \newpage
172 |
173 |
174 | \section*{What are plans for \textsc{Menrva}?}
175 |
176 | Write more examples, to verify that \textsc{Menrva}'s simplistic approach is enough for the real world.
177 |
178 | \hspace{5mm}\vdots
179 |
180 | Use in the real world application.
181 |
182 | \hspace{5mm}\vdots
183 |
184 | PROFIT!
185 |
186 | \newpage
187 |
188 | \section*{What are you going to add still?}
189 |
190 | Something to handle network destruction.
191 | % Currently all nodes are double linked (parent $\leftrightarrow$ child).
192 | Thus dynamically created (and destroyed) UI components create leaf-subnetwork which should be cleaned up.
193 | \textsc{JavaScript} doesn't have weak references (which might be enough to solve the problem).
194 | So we need some manual mechanism for cleanup.
195 |
196 | One idea is to make own reference counting:
197 | \begin{verbatim}
198 | var $state = menrva.source(...);
199 | var $derived = $state.map(...).retain();
200 |
201 | var $componentData = $derived.map(...).retain();
202 | // cleanup:
203 | $componentData.release(); // also removes all callbacks etc.
204 | \end{verbatim}
205 | \newpage
206 |
207 |
208 | \section*{What are you going to add still? -- 2}
209 |
210 | Should we have monadic join? (= \texttt{flatMap}). Do we really need it? Can be worked around, but sometimes it is convenient:
211 | {\small
212 | \begin{verbatim}
213 | $artistImage = $artist.flatmap(getArtistImageSignal);
214 |
215 | function getArtistImageSignal(artist) {
216 | if (!cache[artist.id]) {
217 | cache[artist.id] = menrva.source(placeholderImage);
218 |
219 | // schedule carousel for images...
220 | }
221 | return cache[artist.id];
222 | }
223 | \end{verbatim}
224 | }
225 | Here the cache (or artist image store) can be signal itself -- no need for \texttt{flatMap}.
226 |
227 |
228 | \section*{What are you going to add still? -- 3}
229 |
230 | \emph{Borrow} goodies from \textsc{Bacon.js} -- Function construction rules:
231 | \begin{verbatim}
232 | $signal.map(".foo"); // shorter way of saying:
233 | $signal.map(function (x) {
234 | return x.foo;
235 | });
236 | \end{verbatim}
237 |
238 | \newpage
239 |
240 |
241 | \section*{What are you going to add still? -- 4}
242 |
243 | Lenses. Sub-value of source acting as source itself.
244 | \begin{verbatim}
245 | var $foobar = $source.lens("foo.bar");
246 |
247 | $foobar.set(tx, value); // the same as
248 | $source.modify(tx, function (source) {
249 | return assocIn(source, ["foo", "bar"], value);
250 | });
251 | \end{verbatim}
252 | \texttt{assocIn} -- \url{http://clojuredocs.org/clojure_core/clojure.core/assoc-in}
253 |
254 | \newpage
255 |
256 | \section*{And why the choice for transactions?}
257 |
258 | You able to update many source simultaneously, avoiding immediate propagation:
259 | \begin{verbatim}
260 | var $a = menrva.source(1);
261 | var $b = menrva.source(2);
262 |
263 | menrva.combine($a, $b, add).onValue(console.log);
264 |
265 | var tx = menrva.transaction();
266 | $a.set(tx, 2);
267 | $b.set(tx, 3);
268 | tx.commit();
269 | \end{verbatim}
270 |
271 | \section*{And why the choice for transactions? -- 2}
272 |
273 | Also to mention, transactions arise naturally, if you try to implement anything similar in
274 | \textsc{Haskell} or \textsc{Clojure} (using \textsc{STM}).
275 |
276 | There you will be forced to do all mutation inside STM transaction.
277 |
278 | Changes initiated in transaction are applied only when you commit the transaction: you \emph{always} have consistent data.
279 |
280 | \newpage
281 | {\LARGE the end}
282 | \newpage
283 |
284 |
285 | \end{document}
286 |
--------------------------------------------------------------------------------
/dist/menrva.min.js:
--------------------------------------------------------------------------------
1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.menrva=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0;i--){var next=getPath(obj,path.slice(0,i-1));acc=setProperty(next,path[i-1],acc)}return acc}function modifyPath(obj,path,f){return setPath(obj,path,f(getPath(obj,path)))}module.exports={identity:identity,pluck:pluck,values:values,objIsEmpty:objIsEmpty,getPath:getPath,setPath:setPath,modifyPath:modifyPath}},{}]},{},[4])(4)});
2 | //# sourceMappingURL=dist/menrva.min.js.map
--------------------------------------------------------------------------------
/dist/menrva.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"dist/menrva.min.js","sources":["dist/menrva.standalone.js"],"names":["e","exports","module","define","amd","f","window","global","self","menrva","t","n","r","s","o","u","a","require","i","Error","code","l","call","length",1,"signal","util","Signal","prototype","log","args","Array","slice","arguments","this","onValue","x","logArgs","concat","console","apply","onSpread","callback","undefined","tuple","signals","mapped","identity","forEach","parent","children","push","sequence","record","rec","keys","k","toObject","values","res","./signal.js","./util.js",2,"egal","b",3,"Lens","path","eq","parents","split","value","calculate","initSignal","calculateRank","rank","getPath","v","Source","zoom","set","tx","modify","obj","setPath","modifyPath",4,"option","transaction","convenience","version","some","none","source","combine","./convenience.js","./egal.js","./lens.js","./option.js","./transaction.js",5,"Option","Some","None","equals","other","map","elim","orElse",6,"index","callbacks","CombinedSignal","Math","max","pluck","xs","wrapped","that","indexOf","splice","initialValue","addAction","type",7,"Transaction","actions","commitScheduled","facts","isArray","len","calculateUpdates","updates","action","update","initialSet","updated","triggerOnValue","updatedSignal","callbacksLen","j","commit","clearTimeout","child","objIsEmpty","Infinity","rankK","min","next","curr","childrenlen","childIdx","currLen","currIdx","nextLen","nextIdx","rollback","deferCommit","setTimeout",8,"arr","property","setProperty","copy","acc"],"mappings":"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"}
--------------------------------------------------------------------------------
/docs/20140703-freaklies/figures/data-events.mps:
--------------------------------------------------------------------------------
1 | %!PS-Adobe-3.0 EPSF-3.0
2 | %%BoundingBox: -43 -43 43 43
3 | %%HiResBoundingBox: -42.76968 -42.76968 42.76968 42.76968
4 | %%Creator: MetaPost 1.504
5 | %%CreationDate: 2014.07.04:1435
6 | %%Pages: 1
7 | %%DocumentResources: procset mpost-minimal
8 | %%+ font GJGAYE-CMBX10
9 | %%DocumentSuppliedResources: procset mpost-minimal
10 | %%+ font GJGAYE-CMBX10
11 | %%EndComments
12 | %%BeginProlog
13 | %%BeginResource: procset mpost-minimal
14 | /bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd
15 | /fcp{findfont dup length dict begin{1 index/FID ne{def}{pop pop}ifelse}forall}bd
16 | /fmc{FontMatrix dup length array copy dup dup}bd/fmd{/FontMatrix exch def}bd
17 | /Amul{4 -1 roll exch mul 1000 div}bd/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd
18 | /ScaleFont{dup fmc 0 get Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd
19 | /SlantFont{fmc 2 get dup 0 eq{pop 1}if Amul FontMatrix 0 get mul 2 exch put fmd}bd
20 | %%EndResource
21 | %%BeginResource: font GJGAYE-CMBX10
22 | %!PS-AdobeFont-1.0: CMBX10 003.002
23 | %%Title: CMBX10
24 | %Version: 003.002
25 | %%CreationDate: Mon Jul 13 16:17:00 2009
26 | %%Creator: David M. Jones
27 | %Copyright: Copyright (c) 1997, 2009 American Mathematical Society
28 | %Copyright: (), with Reserved Font Name CMBX10.
29 | % This Font Software is licensed under the SIL Open Font License, Version 1.1.
30 | % This license is in the accompanying file OFL.txt, and is also
31 | % available with a FAQ at: http://scripts.sil.org/OFL.
32 | %%EndComments
33 | FontDirectory/GJGAYE-CMBX10 known{/GJGAYE-CMBX10 findfont dup/UniqueID known{dup
34 | /UniqueID get 5000768 eq exch/FontType get 1 eq and}{pop false}ifelse
35 | {save true}{false}ifelse}{false}ifelse
36 | 11 dict begin
37 | /FontType 1 def
38 | /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def
39 | /FontName /GJGAYE-CMBX10 def
40 | /FontBBox {-56 -250 1164 750 }readonly def
41 | /UniqueID 5000768 def
42 | /PaintType 0 def
43 | /FontInfo 9 dict dup begin
44 | /version (003.002) readonly def
45 | /Notice (Copyright \050c\051 1997, 2009 American Mathematical Society \050\051, with Reserved Font Name CMBX10.) readonly def
46 | /FullName (CMBX10) readonly def
47 | /FamilyName (Computer Modern) readonly def
48 | /Weight (Bold) readonly def
49 | /ItalicAngle 0 def
50 | /isFixedPitch false def
51 | /UnderlinePosition -100 def
52 | /UnderlineThickness 50 def
53 | end readonly def
54 | /Encoding 256 array
55 | 0 1 255 {1 index exch /.notdef put} for
56 | dup 97 /a put
57 | dup 100 /d put
58 | dup 101 /e put
59 | dup 110 /n put
60 | dup 115 /s put
61 | dup 116 /t put
62 | dup 118 /v put
63 | readonly def
64 | currentdict end
65 | currentfile eexec
66 | D9D66F633B846AB284BCF8B0411B772DE5CE3DD325E55798292D7BD972BD75FA
67 | 0E079529AF9C82DF72F64195C9C210DCE34528F540DA1FFD7BEBB9B40787BA93
68 | 51BBFB7CFC5F9152D1E5BB0AD8D016C6CFA4EB41B3C51D091C2D5440E67CFD71
69 | 7C56816B03B901BF4A25A07175380E50A213F877C44778B3C5AADBCC86D6E551
70 | E6AF364B0BFCAAD22D8D558C5C81A7D425A1629DD5182206742D1D082A12F078
71 | 0FD4F5F6D3129FCFFF1F4A912B0A7DEC8D33A57B5AE0328EF9D57ADDAC543273
72 | C01924195A181D03F5054A93B71E5065F8D92FE23794D2D43B233BABF23DF8DB
73 | B6C2BD2F04672F9A3B7FE430263E962F16A948319C51B8ADE6E8A80D3D88023A
74 | 6DEA4D271676C2C8615C4A0EA7DC8F6601610F398673A4D4B905F49EA868FEF6
75 | 39BE073001A36DEA6C08ED51452F062B971740019692E221F4455EDE46AF24B8
76 | 407A98B791F6AD525C72C09776247E194043281D04FE1CD1D8AD8DCEEC3045B4
77 | F95B3B41CD3300768D8A049815348BD7AC1004F5500817E3A267D694AE108BAF
78 | 285B288FC5F28A03E9D34FE5D9B2F9A9BB26ADE66B1CF8EB5BE606E83D213C33
79 | DE083C20D636EF780E761944FCE3B8A950B1E6E7568F33B557C6D59E0CEAF185
80 | 53E609A4F58AC4D5269116F958C4D115C44B5A6DABAB79D3BB6E60BDFCECE108
81 | 74CFBE258779F32C80CD7D9A7CEBA50A0966BD9961F71560119668C4A0C30A5D
82 | ED91ACB30940502B24F33906D6E0F16F81DA87EB6D7FC8B7853BE388C40D75C2
83 | 2CA8F94713AAA1561F5321CE97997CB4AF0E37F44E25B0F73CF4986422B1CD89
84 | 8F861CA623004ADB1C28268D7F8C484AA10C9519B6AEADC95AFAA3304D60E85D
85 | 718B2F67D2B734095E5A92C90785252C98067DC05137BE735220BBCB7C341D61
86 | C4B98BFB1EAF883D38D7A93195A059EF82B42027F23B6CD633231D704B891A9B
87 | 03D11A646F13898F20321D7BC150C63FD6DC6BF9CAFD8DA759E95384B729A0B2
88 | 767B9F3E55C682F7A248BC1184F7D294CFFAE0B246DFCC8F215625DDD4F49F09
89 | FA8D41CBF4A06152FEB152C61539ADF7E70A4D23AF8267D25CE3B12D39D62377
90 | 547E2528D18DC4134FA3BE0437EE0B3509223D71F06D44C6227D62BD01AC0A2A
91 | 3EDA975E894371C07CA1027B102549A7D552FFD25ED2DCC68E29E71BBAB43C62
92 | 57B0BFC6A953ABC2EF703F35D112F6B5460018CDCEAD17F149DBE5B52C2B9E10
93 | 9818EA6D97C8AC884F6841C9B600A7D868F59C1D80E98DE0741D06D69858EC84
94 | 1B33C6C9938B7E8A6FF6C12AD456EECBD3EBAF0D7331536B9F6422019FAFFFA4
95 | 822E79D6D89D6366DA636CB708894FEF904F366E295F1CB808E78E883913C4FB
96 | 1631248ED6A7CF1095C0C61C4F05E4B9DFC47533A5FD24540AD71A0E2907B98B
97 | 28085EB88ABFC3478C9644594C7DC4244ED5A7C1CA8D960B65497D56D174645A
98 | B88F12C2CF0A807DA314017984CF3C4FB7F47392A651EB9CFA961B28E2989893
99 | 9FC4D97171BD5338673F5D1CE2C3C08D48A1B77769F01D5058236C655FFD864B
100 | 80E28F900191D4EB349AA5E400A7B0A0FCA0F3E79D7A7C69775BF754329397B7
101 | D18B20B2683CBC4A19729BA878B3C17EBA0A7E9EE297A5B67E915CAD92C8F356
102 | 582B8299DE6A58E73408F525F7EA895C48A8F0D626A06A96A50348DFBE479D89
103 | 4272576FBB0CD332193D28A8F11503BAE98F8E1D73CF5BCADF23DCD4E6586ABB
104 | 323568F5A34E359661074D50CD8D9DF27191FCE24F10225A5D721EFDE2547E1D
105 | CA998077D2340B1A4ADFFF570AA677CDF3305D5E3A394BB1626EB35074D4EEAC
106 | 2F037CA2EA389F7683FD17A8E07C12B4CB3BA8C249C9B12D297C618009F76717
107 | 0EBF5F2DD39A6BDA10A2E5A811D4E190660F5FDDBA29201B6F8042620397AB2C
108 | E59267A7247B0463891831A6F40582BC3F614E5167B646A8F53D8A31717DD9A1
109 | 9034034E705BA7884F0E0738307AF69D3517147C282747F2788462FDC4336A4F
110 | 9CD222908401A25F0A1F7B13B8DAE622DC965AD0BE62497420B70C04AF432237
111 | E0FDD043456187658ED93B0F9822A3998511DF05E59CC85B7B9992CA0CE3B814
112 | 9723BAE70D2631F32B4BF93511F67179FFAD2075E1591CA5907A4C67701B56CF
113 | A5E5B02EB4A842BA1F18D6864E5677359C2FB4AF5BCBABAFB053F230CC129B45
114 | 8D15413F736EB07C571521C7DE2A13F2AC1C133D491B0A607197BE9AA1231D96
115 | BED7968788246B2E4D2BD330F802810F5BDA3760FEA5210CFC6F54748FB1D921
116 | 5CC3624BBA5B8962AA7D94159651589540B17CF7A785F297264F9C1006D36928
117 | 6E2756D3B623A6087E4B106FBA76255903C624C07E18A1AF4E185A533C640711
118 | 86BB477A906ADD36EB6C8F4A12BC2F01B2B98412E4E105977640930CD998D990
119 | 0254A1E5E9843B7A8ADE0AF6D5871E6D3D666465AE69813A2E26333213FF6713
120 | 6F08D55A90C079A56E1B9AC655F720FC22B5AD8550FFF26DA7B0C5A0B60DDB05
121 | 64E8FAF684F3A455BA9BC9278043D79537D201D520E38750335A4C8FEA887377
122 | 879331B68DAD6B253F4FF9981D0F9B9550ED5179B15EEEB00E560A3DB6E5973B
123 | 63403E4E2F40A3D0B937246E9652000B917B1369741E0F913C14C2D2D6D1FCBE
124 | 2CEC4422177C58523715BD070002EC2E13D383A1DC8C84228862B6C5D3B65667
125 | 9FA97E175239BB7FE7E37E14B96DD7960A8AD49DF428CFC13B5D3CC22E245317
126 | 47B5244DA97F1DF954CED2D552477237CB23D037C0DE728E26C82738954EEA1F
127 | F34FE497DA005AF03746DD2ACF77F6E6F2C224862A1D18AF6F7A5DAF34564387
128 | 9E01DBFF49F8621C058C04C2B3F4F3033FF3E8A977B2CD6B2A3CA4A6C569B19F
129 | C5AC457AE9AF334DA66A730960C7565E93A2D373C0E3DE14646FFDA05DF4C6EB
130 | 6D4CA8ACCA3C3115764F77B842581760BFB9E5C0EBE55308B0577A8F4D968CE2
131 | BA3361D79378D451DD150C34D7E901397AC63B33BD7DB13C50D678F5DE999238
132 | 4B4EA15BD449C46F262D931478F5685CDEEC4C4201FC3EFA607AFB8F27AF6751
133 | 125DE42D2FE2D31DE769B7E7FD8CC8C5D91343B537139A822A5BC4160BB5314E
134 | 37501F65B4FC35475FE9E03E34CBF6795AE86CE409500BD0799DE39FA69978B6
135 | EC74D2197C03632D3F59B85F404DB31240968FA75059B2581B101E028CDECC2E
136 | 7E5E25DFA106E9B8ADB81E82BE9ED3BAA9D03EEB22B7B67AB1262DF6AF5F5EFD
137 | A5627EFEB84F3A5F92EF2557EDA2843D7D18C592635623CEAB14CC3620F33986
138 | 410D6DBAEF9F86E4E6682054540E2B01D8FF2161F10E66851A188BC15BD6666E
139 | 8D3F21709F196A31EE676D28A2D12639CC2E7020A52910F052E61A0710DF09B0
140 | 064171D05611451BD24FAD64716F141E1C41D3218A8115A3D73CA041D02B46D9
141 | 28C3D07DF0FB668E8E91409C8D0A26A65CD737C075E026AC0A974C9BE658199B
142 | 3B9D82ED95E4646977D8F60717DA4C68767DBD7E8320D5AA1D5DEB2E6B009759
143 | 8282F27D64F1F904830AAB501CDA4D9233FC2F12F77F0FBCC46E6B729C71F6D5
144 | E6F3EA02EC35D1048394F4EF2177FC5EB726DE5EF2DE7997166B8BE5B5105D08
145 | EAAC3481FC612665CA112D3F889A0E5B7843EFFCEFACA24A01B6AC2B7DDE02F4
146 | A9295AA2409A3756BAAB44608DACBB56840060037869455BEBA46F10AFC68DD0
147 | 0563843DF111C6D34911CF13AA6023E5E899060B5EC60D0F78FDEF3E981151A9
148 | 24903EB13ED1A67EA1977449716D1A5A7EDE1A2E9465C9C2B20A58AF02D9F373
149 | 73E627CBF296B3A6A4670C39F3B5EA30D76F0362C81020A1777F0ADDBC6B52F7
150 | 213FEE1718214087837049CF2AF00407639657428B9E8B532F68B631611A3501
151 | 3D9DCA38090E227BD0D6D0FB4130EE866DB6B195C873AFD18DDB3B1E40F740C6
152 | B3B375ADCBBF628A07A5FACED539FEDA3379D3B60216C2EA6629BE2F65199D82
153 | FE3AE627D7C67270F3497AE75F7A9514968B5950E2D63C38DA240AF4E6CAE88E
154 | E25167D179108679876E7C80C85FE1D2BCC2EC9B88BE76A8F5736E8E6B3A9CF9
155 | 42E58A4ECB7914865E67C1468CF66D658206830B9380FE346DC2DC4BB56A92CE
156 | 4B5E4EA9036C177869315A2D9E6CFE97E3BFD7CBE0747D40CE5E8A3A0988576B
157 | 8AD2B1E4314C0D8A0CBCA08844A49F7E054D31BA7543730C0A7390BC4A288D10
158 | CE29E389A4791305D3AC1BB6F77C805F1032787306F78FF76A20A9E629899F6D
159 | 13356768D33D7B9E294E8CD50CBFB9CA02A193922BD9B4372C912D1689B6644D
160 | 52CAA30F7421E8114D077288119AD9514EF21E5B9989CCE2ABA0C12549FDF493
161 | FFB39736AC9EB72DAF45E4EA6057527FA9F5AA0A1A3F03C12F7482E465C766D3
162 | 760DA7714D56C91BDAED507A5572BEB51A895F8DD3BD5AAB042650154FC7E4E5
163 | 5EEA6194DF73AC5EE2CBD4EE26E29B1D2D0C458B4850BFE842DDF2EBB4E2A25D
164 | C6A11CA2D8F346E2B736DF88A3D57BC0380B52396A6C039212699F5D3342EB58
165 | 0C3DD5D01D5078479BD9FD10C07925556C0AB0F03606F33796BA72074549EDA6
166 | E33644F62CA35207D7421D2727AD8419AD1772789D33405FCDDC9286BC34C974
167 | A52297F5BBD2E541E8BB473F733AE5097BBC9D5FACF18DE4173B4711E28B23ED
168 | 16E0A6746A60F6FF903026A3900169EDA87D98396E762C2EC963D89197B8CD0C
169 | 25244806BE7CBF46BE60A8F9171731EADFC969C28679B025371E5572E52A0EF8
170 | B3FD9B4638D03E20BFDEC9345E70B8166D38846DCA68E0D0B4B53629C7E7620B
171 | 45E0A610BCD07FEF8814CF915CFB11119F42407D1C6DC1E6353451D40A382C2E
172 | C74DF2A4889ED5A3495C3E973565F7178CA190D22C9693C10EB12C1E7A8679CE
173 | 4AFECFC964CC98111BA4ED2BA9B10292A71D5B11870EB08EB483922CE8628A06
174 | 05E7CF6DF93E112B60EF888AA8DB52994EC33DC7277D7B7A4F913AD30257261A
175 | D6EE80476A9A8D316D190BE6CE0046CBBCED365AB305495284FA921BE0638E00
176 | 63DB2AA4C5F163340BCCD1061B469504DEE350B82FBE1689C1B65D095405614B
177 | 35997D6F0DACA7190D64ABA351705B17B23FE2EE5996FCD607F49F54392463EC
178 | DD5B944A4B82FA2BE3E75E2946D483060DF99277340B0AB65A2042AD088E2B75
179 | BBDAB869D1940F64B50D25078519D18748AD64AC5615EFAAF4F3105B0111AD40
180 | 70EE173ABE6A4ACE486B4E5999158A4377FDA6922FAA6E9305F48570D14BC81F
181 | BFF4C663E1EA9D1E050534F9315A663C4C5DA52CB02EA6408AA473C32CB0CD71
182 | 169BB43C0508A842F400240F0063243B4C459A1FCB3312C41C32ED0EE87F591A
183 | BCB6D5D3830AE4645CB4D40336DB4AB6540B52E70E1EA415CC6D886827EBC5B3
184 | EC35CC5C136243B0C20B3C603B648B132B99D05F9B48263ACFA59A856BE74441
185 | FECF5C6D1FE9D1F4F9942F460961901E16017144C37E83C6822177B2A6C47ECC
186 | 6C47A1104460665E5BCFCF08874008302750EB991CD98D0D8D22B921F90B99B9
187 | 05EE7C39F2BC2A7798157503743C9F2F267BDBE2E8A4CDA7317F81DBF8962E1C
188 | EC02822CC7F770FD4D08D335904375BF0C6DAA0510771627ECB9EE69C0F47D30
189 | 69A87052989DF80D9F4F19F75B070C3689AB3BE0966453F9D56CED6C1745B50D
190 | 813AE6D7E44B73423AB3778ABE4CD2C4DF40E14C5A426043F7057E2DFA2DAA70
191 | EA6723F1C7967FECB1E7C1C0CA283334163FBE31C32254490170C3513580A552
192 | 19A5DD75E6C4ADCB12D33517A03318A6BBC7E4214266E125140D8C40F78A0340
193 | 1F95D9FBEC4DCC55B71E89375AA94B0D55646F6C069561480407D0A3AC127024
194 | D7D1E9ED6B599A2A8766B8792F46D35508B66F302D289405B101A3C6BADA680D
195 | 8C56E2A00B766A4CB155446F862FCF17537A2BE85418E20CD77C4F1F69F70BC6
196 | 17BB5DA8FAA876D0E8BABE273A19C04A8697B3E3CF4725E2C77C8761A9243F24
197 | 96F8AE96399996001A57FD75106745AB4646FB9C6421F1D4EBF3BE533BD11AE8
198 | 14BFBD6D308376B26E08E4ADA490DDCCA94BE8240403D5EB0FE3549061DFB668
199 | 4105B4FE77189546619B6BCF3F9723E278E98D50A17DB8A4C46744FA21760635
200 | 5B332689316BD17C966D466AE737FE3ED7ABC443ADD88D4823A10BC9747ABDEE
201 | 027515AC353A420523F85298029475D8BFD83A2CD00C02CA07974BAA581D2215
202 | A850E6E4C0A5E17E0EDF91C63FAC18C70093F40FEEAF0350B403E2806F4EAE96
203 | BF616A805616EE55C4657418C26CAF54187A6684821B86A76F15088AC4D5B551
204 | 66C3CA8DC61E9810858D1204F899C7E3A1754F483134609F6EEE6364B1CC04FD
205 | 92C86EF194FA3249601AD722D75D1D395CD15A93C768EC60A486AE885683364F
206 | 93DA00A865C1035F913FDA69E7D9A0422880FB81EC23C00427F07A5EA3CCB613
207 | 83C859958AC53FAEA26A6BB39ABA068863CCE3D447720BC31A5136E08EE58963
208 | 093AF587A72112D55853A1048A2B1695DB2D7F13CC924F2F0902071260C33ED6
209 | 30893A04577C0ACF0681C0FEC23E5404F844A83BB5A2F8DE1F0792196139993C
210 | 1152094BC36A6AE5BAB4B8281E506F485F0BAEEBBE71E174B7CED1E52A8242F9
211 | DBDF3E0FBA296538D964EB5B7A4E23B1BB0F59872C9D4FE8498203E3AC08B71E
212 | D2B965AA6CD5D20DA6C83FDC86F4A5D68A58A72C5BB9BFE9BC755C56B24025CE
213 | 6F24443D3CF32CD711D3D94C7C1DC275DDAE3341D840090F97CB6CAEF274C94F
214 | 9F3BD3AAB3F98BA8A7B5CE5E48D1462DAAB37BEB1C10B410E8D33FA42D898183
215 | BD4F807112D78AA94509E33C179BF7C9E82E55AA7D09E128A0DA06A330CF4AF8
216 | 5DC861498CE029CE8C1BD15C923A708F2E7AF98E4F7B34212A0CB417553C86EB
217 | 6DD46B0466F1A21D29FC5111226794ECFCA5DD4240C0B8D106CCD7EA6F03E133
218 | BB7733F055D6FFA04EF5C6F872B4FDA3E42F0F036C4825543D75682ACF71B548
219 | DED160ACD05625274799D0AE201305DA526E01A3D2A719B1B15C05CC09467F3A
220 | 5627860C0F36C503EE392E1786620F3F2287AFE56634E03566B9B1F537FD92A2
221 | 913166228791871A8F8CBA1A1DA634E8224058052A10FE1E67CBD3FD21A6C07E
222 | 243CBF58BDC78577847664EEA5225EB8D6679AB17C563848A9D4D58995EA3609
223 | 51C1443B752A070D9872FE1643F0677019235AC25DC2B29169D38308F2170A1A
224 | A0FDCC59E6602197D2815B914041FFC7106DAAAF30CD97400C6D0826A40385A4
225 | C8520119A065CF32CF2FC5FBD8DFD29222528A7F96FDA533145846B3428F8239
226 | E50277C366418D713F84B12A5FD4F904DC13DB1844A391FDAEB97643A6FD2945
227 | 942FD4FC5A4A35E184F23304B8B4D93D0C37EFCC4E106D4FCD0DA3E5D2117589
228 | 3FFC2BD1D121026562C55C455C3585050B9460891B006F62D9D9B66695C3D348
229 | A467C14C0256FA9621CB056E7CD389505194FF463BCC4010897F9A690EA87D9D
230 | BB3ED4C174FBADB8A4744C6E4A44D773967FD703EC37672F9993DC48BCC8A060
231 | 6CEFE8E6B8F10886E15BA0466AF410B90DF0020FAB88BE493606B6A734EA85BB
232 | 926950EB10D2F2CFDBD182B0F133809612CCF6ACCAD049C8005A42FAF78368B9
233 | E7684F98DE421BE0A3BC0FAEE024A7BE67E15C8394F17FE84DFD8156C2A3E94D
234 | 08259E15CC657E8CE3088395BF6B5F825764E141AE15EBD186DC049261623D26
235 | 8636705E06C6E4A1F8ACAEA59F91B042DF5DB9C2AB986A784384706A43E5F18E
236 | 42C29CC1CA86D4F247B3BBBC89F3633EE074DCA4AC15B1E33EE4822812A62E88
237 | C32B0AA57249980EE17AFC1346074800FA529445D18649A0475246A25CF325A0
238 | BDA06AAF392FD455218B13D9ED577D51A9500B9FB7860716A8E2FB3A8C4BE3B3
239 | 6656C6A5653AEF00184020ACA0BCCBF48BE3BF91E11C8658686C89848E714E6D
240 | DC158DCD1C1BC03B83FF94C60B1DC71CE8A86B46DBE661C9F8F4677F8A2C7CF1
241 | E41A91EBDA2304735147BE66CDFF2673F09D408297302124C127F0B35690CAE9
242 | CE1679120CC4D582FB69550AD34A047DDFCD9D411724554CCED753DB52D6AA7B
243 | 22B0C55EB698ADDBB0F8ED15C971AEF113C74B9E25DA29199237B98DA4023665
244 | C2A63A837E4CAB38F8DF37DBAB5DC80C0C3FA72C8A70DC76B5B36B2EEADDCE74
245 | 23CF794B66E4DD3B35BF99893789063BF7B01D5F186B2FDE518B2CF2EDE51F81
246 | 38244BC64548AC3433A80B86D6A0CA26D77F403C06D65B7394BF1FC7D06D37A5
247 | E70ACD844E3367DE4DB71312CBB85ADD21D5A1F99BB8427F252D90ABB66D7154
248 | EA5AF4A165DF6415A0880AE784071E6B3E2101F0B663DE14DB1ABF8B7CE0E6D9
249 | D24F9CDD9F80028D37C9CB4067A28D41E879AEFECDA71F649EB3C250BFF809D8
250 | 1E427E3BF24E85C75F080D93E0314883988B3A4A2B72A1B4A3D2189AB6ECFFBB
251 | C58151AF05AE335200711ED945E18B4BBCE24A8A162BD9BB26137253BA8B5819
252 | 41E759A7CA7CBA129BAAD438E87189F2F6AE7C86F4EA099DEB23705A500332A7
253 | 4F141D8778EAF3910486B2EA25AAD16B60DD804D0E5BAB0FEBB77BC95EDED08D
254 | B8941E040D99E8F44E70FF842306ACCF65C0AC9673859DB9C3A724238CB8CE62
255 | 255BAF0145692EE3B52643A0DE3E667AD03EEF6C753F57E34AECAB0CFEC7B07A
256 | 150D7151E57BB3A026D50C7A88DF5F480147D87DFEFE463F76122EEB5128796E
257 | 46CB0AF4B537987C2ED552B37D83F393222659DB735F2A293159AD84AF082B95
258 | 6F1454471FC36D805485D619D58FC53FBD6E3F72660ABA559B91ECDEFB267268
259 | 86A75650C3919962B0139409A29F5E3FA70B901CD5D2C49144778CFFF1D5B63E
260 | 099C92AABDAA73D54689812279C95FB7A4F7E840DD53DD3197A4E6D3099446FA
261 | C0032FD40411E8F3300A8A8934B5216B01D916D41DDB32513DC4ACFCEFE43D6B
262 | 22FEF13D3567B047C6B35C477ACF2E172701FDB0FFFBE01DD58D7E54398EF4AF
263 | DA5A404E194BCC39BEADEE5C76D7CD1E602793B950256F25871A9760C80B1EB4
264 | D1E1179C390BC240DA061C9D539B20F4FCFB72DD0C1E860DEA2988E749819787
265 | F04BA7A9CC3EDBF9CDE46895FE31EF0F8DEB63E295E8826BF920C8FEAE3B2080
266 | 8C98DC43DB22C6537028798198E2D3B0453ED725B774686310F635AE6153D9E1
267 | 8A0514882D4CCFE9D2D2465513E42E548F64A50ED78AEC9D62E0F9CF61EBFC9E
268 | E8832D60E91796C916FAFE58F51818B80BBA52C1C06D94E602481654E5378C8D
269 | 137E3A872753CCDE4B2618C031CFB13EE91C91335441C434296DDEF61CDFBF8E
270 | 8FAF25DB3B6D6796FCCE2711938D605AAC00F0A58DD1A03FCE8732DE541E5E8A
271 | 41FC87E1FCA5CD9B5E8D63E7A7D6CEBA67D8A83EAFCF490DB7185AD55ED0F43F
272 | 9A1290E91C463895BD12E8A831DAD661E36E1B01ED4C112B8E1D0991D0294BB9
273 | A13B7E9A8835B12A7133E834379B3477DAD425B7996592FB0395E3B4FEDADF4E
274 | 23A07F6C0E1387DD54F5C8BD071C4E9E4CC98C55882E29B65E5BED61B57EED93
275 | 07FAFD75CF3BB101D1529F83AA8234F70F342B0E531BF23E9A7D1FD112193CAA
276 | 4E377B44F94D9E990C22598C2AE33EA73BD0670A4A000DF78624CE01A25DB30E
277 | 22B0EE3FA892AF673E323BE5D63D0929903FB45F56B11CC718EF5A690A776E28
278 | 8BEED7010ADB1076B23257F9484533DB8A86604B70B9F2A25A7DD70F13B65172
279 | 5C3DAA77885AE348CE4EE02021BDDEF16B58AA7B006877B630C97653DE7C661E
280 | D5D3477A1BBEB61FFB1A113E316EB366EBDDD54C621987FEF09432023A2EA97D
281 | 0B4D38229799F6656F7BB8E67379FC4652ADF65179AACF7FC9CB925A84ABECFC
282 | 00B4212CDF4E88876C2DD4E79099677C4A5A97860D9D1615587D68A4AC7A7204
283 | 47E421FC84C21560E71E1FC313776DC3717FB397BEC643F896029B671A199342
284 | 6378241B3230E538EA282FD38ABE642223D82A657245105757BE84B599C63F2B
285 | 008CE276FC313D6A101898D1A6BB0AD8D5662A85E965F7E78373DDFD3C560DC7
286 | FB06BB67D226D4263EE9491F90E104FC210E9E3DD63F800300FA77DBF9FD0C93
287 | 0B51906FE3A19B317816A4402F7733C8CE2C7502D915352458CFD581FD484523
288 | 62678E94F04F8882948A1C35B484047CAAB70808134426970C83A1C4698C66A8
289 | C900013C14ED7BA85AA037F846CD7C05F0C4B63DC1C10E03B9D34C6727AACF15
290 | 4653EEE73AD08E20791CDD3CF827BA903093EB249DA239F1DFE9775FBAA1495A
291 | 572719400771332BF0154A0C26166D5A49AD37BCFC2E5AE335976E04B58E7646
292 | D2914E385942BF82AF3E3DC997624DC06C800A6F598F073FCAFF807F256B3112
293 | 82C18E8068ED0C375669081A6386CE0BC4F6A3CDF009DBD0976CC1C9D0C7C8B6
294 | 147928A042882E1E8076D42988AB29A83A2458F3CFEE4D4DF2FA84E2F4388F7B
295 | 9FAE2D16C78DB978CA99B7CD78F3DE7518A6AF93A4B40C506C136E5A5F56AEA9
296 | C6C4D28960B5A34CBF2A115403A2E98751715251D4CF99D8DB478BEA75D64BD6
297 | 3E250047E910F5587847B9B4F6BC43819114D9530E8A6887BD14D5580C88C587
298 | 1E9038CC6862A415E904219926DB8079F7CCDC19CBE16973BC49D80BF0F83B4E
299 | B5D4796E42FB7053D4CD9FEEB842D350BCA07575CE998B37E6594CC0A406B20C
300 | 54AE6DF2CA7EEE4F17AAEF29B1E846118AC9F0B2E42C04E3E4FFB1AFB1D8DCB2
301 | B603A43666989974543B3403BE4C0F1E2E9A6A644510454A0A41DEBAE7BF7A16
302 | F17D7ED921FA514C6B9EEF4C5804E7FDFBB5D0A20D24A42737244C9FD4BAC0C5
303 | A0DDEF520D8F11FBF9BAD49EB6AA56C58AEA63B1DE7CEFC00134596638F0312E
304 | A4E4889A6B53CE50F4F1A779EB21F89BE7A498AAEF4F1CBF32C677CBA5B7E1B0
305 | 74AD2A4B6D241002CC42F60DAAE1AA47F6E62E040DA7DB5EBBF4E92E3A44B1EB
306 | 69C07BCB431ED5309F0C40EAC810A64EC8DEDB0B882F1BF80AAB55232C013EF1
307 | DB00E07C9D056792BF62099824474D3A425032182D82767BE860B5A5E9039E91
308 | 62DDBDC562999AE378DF41F4ABF75A6EC87BEDD4D170867E048831A80BDF18D3
309 | 62B0E17CAAC2D341CA3CA1E587A0CE3B2FF777F6F48D679859F4D46B8BD489C4
310 | DBF23BB9D8AEC3E80C08D92B04857F0A0284D0FB9F7F08A2C146A040FB47291E
311 | 14B3231866D2553383A0ECBBB7E7E2D61F448A6F8B3DA1D5FEDBA71BC26F5816
312 | D94F052DDFC5CE593B8500B83522DDDEC7ACD57B6FA9C9FF0939D7E9E348DF5A
313 | 13B4B8FB575789DA50E9777C09DA0E06BF9729D26AFA0626A9869415B6F57593
314 | 3590DA6D78D23C54C6B132080836DC3B8B142A849BBEDDBC291AEFE5F4A12134
315 | A8479BCAEB272DDD32D44966F4438C181FF987B9B8F2AAFF2A23CF53B12E7DEF
316 | AA6C1ECB39E0717C291DE7C860A6C7FAF1AF1F735C820FDC589591C61A0C9A91
317 | 7EFA971BDDF8072B23C90B2D47FCADD515E7E64E81365F8BE470AED1E001A07E
318 | 830AD52A560AE243D9153860C25B8FB6C14A5E03874A69ED8BB13DE79A0B265D
319 | 2E2623D693805EE38EA14A75219ED8A610A827901B39F4D912F7AC6AE0ACD519
320 | 387AF5A3063E4CCF5213D1C0C9F049098089162B0C6B532C5011C15FE5AC5260
321 | 658D5913919E9DE5C77B86EA9D561F57DA1C0FB2CB40E13FA4E79E72D95D5D42
322 | 56ACED0107B74A5C408FDC2DD7272A0B462EEB7CE6915D7734DE823132D4766F
323 | C3E9CA6D6B8DB40F44148524D979013953D158208890D1FF50DE71B19889C067
324 | A88ABA5BA95B1E501AB399AF2C6B076351DAF37ADFE0256D643CBC589E93D00B
325 | 1AB478482F9041012AD73BE96025F5F837BEAE61196EEB161803504844067AA6
326 | 1909FE4DA3CD8387C9C44D40EFA159854BE845A7D9E93A60B6C7B256E4D65086
327 | 34B4195BCD1E7DE8B4A0EBA9D5C17702199E27116E29B761489CDD720A8E4592
328 | E3286A0673790251C793E97A8D8E553332B08E85B240E0B56FE553D12F98A6F3
329 | 88213670B0A1A1AFAD294977296F6CF309DA03D9D4CCC28E347A0505897B57EF
330 | DE926002B5F174F607D390C010EB2508E923FDBA41517DD465B2273E4EBFEC09
331 | BC5C9E7302EBE72B798C9CF080D0B9156CBCDCE05FCE9CC11405203BC774FB98
332 | 4ADEB79258C48CDF801BF41C64260706ACE27FF66E97129BF509F326DC9A4D4A
333 | 7B9C884237AEBA67251EAD29ACF49E7A9089B0C7C5EB8A75656D2673B7DEC3C8
334 | 0D28C656A52E126379A165479BE78E1750A33881D1139AD0702B4F8EDBD52BFB
335 | 3CCD9F29136536DE8CD677F29A0FC188783E85354CF17F2AD464D15C51574D3D
336 | 36DB3C8768BE1F9C7E183BA5CEF0AFCFB42C94512101D595103A9DF37E8427F9
337 | 35199FE6DB4624EAFB396D54D576670A83E16175D20BA8FC33546076E373E87C
338 | 39C05041A854B7DB99413B0B56757507C514A5FA35B1C899AFC32695D77BFC0F
339 | BC69AD9AA9D38A32CC46CF4CE4C1BDC8F18252287C7834FEA7D12F8C0C30104D
340 | 95B042A94F44F50DE7DD3904447A66BBF478CE1CB2A6BD6CF71B6CDE122F4B53
341 | 180D1B9472CE7E0CABEB6147FD766DDBAC319451B9FBC6AB1CEDBC6421246FA8
342 | 9366C761FA1F929F41F1AC607935F19813DC7A3A53
343 | 0000000000000000000000000000000000000000000000000000000000000000
344 | 0000000000000000000000000000000000000000000000000000000000000000
345 | 0000000000000000000000000000000000000000000000000000000000000000
346 | 0000000000000000000000000000000000000000000000000000000000000000
347 | 0000000000000000000000000000000000000000000000000000000000000000
348 | 0000000000000000000000000000000000000000000000000000000000000000
349 | 0000000000000000000000000000000000000000000000000000000000000000
350 | 0000000000000000000000000000000000000000000000000000000000000000
351 | cleartomark
352 | {restore}if
353 | %%EndResource
354 | %%EndProlog
355 | %%BeginSetup
356 | /cmbx10 /GJGAYE-CMBX10 def
357 | %%EndSetup
358 | %%Page: 1 1
359 | 0 0 0 setrgbcolor
360 | newpath -42.51968 0 moveto
361 | -42.51968 -23.48297 -23.48297 -42.51968 0 -42.51968 curveto
362 | 23.48297 -42.51968 42.51968 -23.48297 42.51968 0 curveto
363 | 42.51968 18.1975 12.39862 24.79724 0 0 curveto
364 | -12.39862 -24.79724 -42.51968 -18.1975 -42.51968 0 curveto closepath fill
365 | 0 0.5 dtransform truncate idtransform setlinewidth pop [] 0 setdash
366 | 1 setlinejoin 10 setmiterlimit
367 | newpath -42.51968 0 moveto
368 | -42.51968 -23.48297 -23.48297 -42.51968 0 -42.51968 curveto
369 | 23.48297 -42.51968 42.51968 -23.48297 42.51968 0 curveto
370 | 42.51968 23.48297 23.48297 42.51968 0 42.51968 curveto
371 | -23.48297 42.51968 -42.51968 23.48297 -42.51968 0 curveto closepath stroke
372 | -32.23944 -3.45926 moveto
373 | (data) cmbx10 9.96265 fshow
374 | 1 1 1 setrgbcolor
375 | 5.63379 -3.16275 moveto
376 | (ev) cmbx10 9.96265 fshow
377 | 16.61339 -3.16275 moveto
378 | (en) cmbx10 9.96265 fshow
379 | 27.91129 -3.16275 moveto
380 | (ts) cmbx10 9.96265 fshow
381 | showpage
382 | %%EOF
383 |
--------------------------------------------------------------------------------
/dist/menrva.standalone.js:
--------------------------------------------------------------------------------
1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.menrva=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o signal.log (@ : Signal a, args...) : Unsubscriber
21 |
22 | Essentially `signal.onValue(console.log.bind(console, args...))
23 | */
24 | signal.Signal.prototype.log = function () {
25 | var args = Array.prototype.slice.call(arguments);
26 | return this.onValue(function (x) {
27 | var logArgs = args.concat([x]);
28 | console.log.apply(console, logArgs);
29 | });
30 | };
31 |
32 | /**
33 | #### signal.onSpread
34 |
35 | > signal.onSpread (@ : Signal [a, b...], callback : a -> b ... -> void) : Unsubscriber
36 |
37 | `onValue` with signal's tuple arguments spread.
38 | */
39 | signal.Signal.prototype.onSpread = function (callback) {
40 | return this.onValue(function (t) {
41 | callback.apply(undefined, t);
42 | });
43 | };
44 |
45 | /**
46 | #### tuple
47 |
48 | > tuple (x : Signal a, y : Signal b...) : Signal [a, b...]
49 |
50 | Combine signals into tuple.
51 | */
52 |
53 | function tuple() {
54 | var signals = Array.prototype.slice.call(arguments);
55 | var mapped = new (signal.CombinedSignal)(signals, util.identity);
56 |
57 | // connect to parent
58 | signals.forEach(function (parent) {
59 | parent.children.push(mapped);
60 | });
61 |
62 | return mapped;
63 | }
64 |
65 | /**
66 | #### sequence
67 |
68 | > sequence [Signal a, Signal b, ..] : Signal [a, b...]
69 |
70 | In promise libraries this might be called `all`.
71 | */
72 | function sequence(signals) {
73 | var mapped = new (signal.CombinedSignal)(signals, util.identity);
74 |
75 | // connect to parent
76 | signals.forEach(function (parent) {
77 | parent.children.push(mapped);
78 | });
79 |
80 | return mapped;
81 | }
82 |
83 | /**
84 | #### record
85 |
86 | > record {k: Signal a, l: Signal b...} : Signal {k: a, l: b...}
87 |
88 | Like `sequence` but for records i.e. objects.
89 | */
90 |
91 | function record(rec) {
92 | var keys = [];
93 | var signals = [];
94 |
95 | for (var k in rec) {
96 | // if (Object.prototype.hasOwnProperty.call(rec, k)) {
97 | keys.push(k);
98 | signals.push(rec[k]);
99 | // }
100 | }
101 |
102 | function toObject(values) {
103 | var res = {};
104 |
105 | for (var i = 0; i < keys.length; i++) {
106 | res[keys[i]] = values[i];
107 | }
108 |
109 | return res;
110 | }
111 |
112 | var mapped = new (signal.CombinedSignal)(signals, toObject);
113 |
114 | // connect to parent
115 | signals.forEach(function (parent) {
116 | parent.children.push(mapped);
117 | });
118 |
119 | return mapped;
120 | }
121 |
122 | module.exports = {
123 | tuple: tuple,
124 | sequence: sequence,
125 | record: record,
126 | };
127 |
128 | },{"./signal.js":6,"./util.js":8}],2:[function(require,module,exports){
129 | /*
130 | * menrva
131 | * https://github.com/phadej/menrva
132 | *
133 | * Copyright (c) 2014 Oleg Grenrus
134 | * Licensed under the MIT license.
135 | */
136 |
137 | "use strict";
138 |
139 | /**
140 | ### Equalities
141 |
142 | #### egal
143 |
144 | > egal (a, b) : boolean
145 |
146 | Identity check. `Object.is`. http://wiki.ecmascript.org/doku.php?id=harmony:egal
147 | */
148 | function egal(a, b) {
149 | if (a === 0 && b === 0) {
150 | return 1/a === 1/b;
151 | } else if (a !== a) {
152 | return b !== b;
153 | } else {
154 | return a === b;
155 | }
156 | }
157 |
158 | module.exports = egal;
159 |
160 | },{}],3:[function(require,module,exports){
161 | /*
162 | * menrva
163 | * https://github.com/phadej/menrva
164 | *
165 | * Copyright (c) 2014 Oleg Grenrus
166 | * Licensed under the MIT license.
167 | */
168 |
169 | "use strict";
170 |
171 | var util = require("./util.js");
172 | var signal = require("./signal.js");
173 |
174 | /**
175 | ### Lens
176 |
177 | Lenses are composable functional references.
178 | They allow you to *access* and *modify* data potentially very deep within a structure!
179 | */
180 | function Lens(parent, path, eq) {
181 | this.parents = [parent];
182 | this.path = path.split(/\./);
183 | var value = this.calculate();
184 | signal.initSignal(this, value, eq);
185 | }
186 |
187 | Lens.prototype = new signal.Signal();
188 |
189 | Lens.prototype.calculateRank = function () {
190 | return this.parents[0].rank + 1;
191 | };
192 |
193 | Lens.prototype.calculate = function () {
194 | return util.getPath(this.parents[0].v, this.path);
195 | };
196 |
197 | /**
198 | #### source.zoom
199 |
200 | > zoom (@ : Source a, path : Path a b, eq = egal : b -> b -> boolean) : Source b
201 |
202 | Zoom (or focus) into part specified by `path` of the original signal.
203 | One can `set` and `modify` zoomed signals, they act as sources.
204 |
205 | ```js
206 | var quux = source.zoom("foo.bar.quux");
207 | ```
208 | */
209 | signal.Source.prototype.zoom = Lens.prototype.zoom = function(f, eq) {
210 | var mapped = new Lens(this, f, eq);
211 | this.children.push(mapped);
212 | return mapped;
213 | };
214 |
215 | Lens.prototype.set = function (tx, value) {
216 | var path = this.path;
217 | this.parents[0].modify(tx, function (obj) {
218 | return util.setPath(obj, path, value);
219 | });
220 | };
221 |
222 | Lens.prototype.modify = function (tx, f) {
223 | var path = this.path;
224 | this.parents[0].modify(tx, function (obj) {
225 | return util.modifyPath(obj, path, f);
226 | });
227 | };
228 |
229 | },{"./signal.js":6,"./util.js":8}],4:[function(require,module,exports){
230 | /*
231 | * menrva
232 | * https://github.com/phadej/menrva
233 | *
234 | * Copyright (c) 2014 Oleg Grenrus
235 | * Licensed under the MIT license.
236 | */
237 |
238 | /**
239 | # menrva
240 |
241 |
242 |
243 | [](http://travis-ci.org/phadej/menrva)
244 | [](https://www.npmjs.org/package/menrva)
245 | [](https://david-dm.org/phadej/menrva)
246 | [](https://david-dm.org/phadej/menrva#info=devDependencies)
247 | [](https://coveralls.io/r/phadej/menrva?branch=master)
248 | [](https://codeclimate.com/github/phadej/menrva)
249 |
250 | Ambitious data-flow library.
251 |
252 | ## Getting Started
253 | Install the module with: `npm install menrva`
254 |
255 | ```js
256 | var menrva = require('menrva');
257 | menrva.some('awe'); // some, as in awesome?
258 | ```
259 |
260 | ## API
261 | */
262 | /// include signal.js
263 | /// include transaction.js
264 | /// include lens.js
265 | /// include convenience.js
266 | /// include egal.js
267 | /// include option.js
268 | /**
269 | ## Contributing
270 | */
271 | /// plain ../CONTRIBUTING.md
272 | /**
273 | ## Release History
274 | */
275 | /// plain ../CHANGELOG.md
276 | /**
277 |
278 | ## License
279 |
280 | Copyright (c) 2014 Oleg Grenrus.
281 | Licensed under the MIT license.
282 | */
283 |
284 | "use strict";
285 |
286 | var egal = require("./egal.js");
287 | var option = require("./option.js");
288 | var signal = require("./signal.js");
289 | var transaction = require("./transaction.js");
290 |
291 | // extensions
292 | require("./lens.js");
293 | var convenience = require("./convenience.js");
294 |
295 | // version
296 | var version = "0.0.7";
297 |
298 | module.exports = {
299 | egal: egal,
300 | some: option.some,
301 | none: option.none,
302 | Signal: signal.Signal,
303 | source: signal.source,
304 | combine: signal.combine,
305 | tuple: convenience.tuple,
306 | sequence: convenience.sequence,
307 | record: convenience.record,
308 | transaction: transaction,
309 | version: version,
310 | };
311 |
312 | },{"./convenience.js":1,"./egal.js":2,"./lens.js":3,"./option.js":5,"./signal.js":6,"./transaction.js":7}],5:[function(require,module,exports){
313 | /*
314 | * menrva
315 | * https://github.com/phadej/menrva
316 | *
317 | * Copyright (c) 2014 Oleg Grenrus
318 | * Licensed under the MIT license.
319 | */
320 |
321 | "use strict";
322 |
323 | var egal = require("./egal.js");
324 | var util = require("./util.js");
325 |
326 | // typify: instance Option
327 | // Option type
328 |
329 | /**
330 | ### Option
331 |
332 | Also known as `Maybe`.
333 | */
334 | function Option() {}
335 |
336 | // Some
337 | function Some(x) {
338 | this.value = x;
339 | }
340 |
341 | Some.prototype = new Option();
342 |
343 | function some(x) {
344 | return new Some(x);
345 | }
346 |
347 | // None
348 | function None() {}
349 |
350 | None.prototype = new Option();
351 |
352 | var none = new None();
353 |
354 | // Methods
355 |
356 | /**
357 | #### option.equals
358 |
359 | > equals (@ : option a, other : *, eq = eqal : a -> a -> boolean) : boolean
360 |
361 | Equality check.
362 | */
363 | Some.prototype.equals = function (other, eq) {
364 | eq = eq || egal;
365 | return other instanceof Some && eq(this.value, other.value); // TODO: use egal
366 | };
367 |
368 | None.prototype.equals = function (other) {
369 | // only one instance of `none`
370 | return this === other;
371 | };
372 |
373 | /**
374 | #### option.map
375 |
376 | > map (@ : option a, f : a -> b) : option b
377 | */
378 | // :: fn -> Option
379 | Some.prototype.map = function (f) {
380 | return some(f(this.value));
381 | };
382 |
383 | // :: fn -> Option
384 | None.prototype.map = function (f) {
385 | return none;
386 | };
387 |
388 | /**
389 | #### option.elim
390 |
391 | > elim (@ : option a, x : b, f : a -> b) : b
392 |
393 | */
394 | Some.prototype.elim = function (x, f) {
395 | return f(this.value);
396 | };
397 |
398 | None.prototype.elim = function (x, f) {
399 | return x;
400 | };
401 |
402 | /**
403 | #### option.orElse
404 |
405 | > orElse (@ : option a, x : a) : a
406 | */
407 | Option.prototype.orElse = function (x) {
408 | return this.elim(x, util.identity);
409 | };
410 |
411 | module.exports = {
412 | some: some,
413 | none: none,
414 | };
415 |
416 | },{"./egal.js":2,"./util.js":8}],6:[function(require,module,exports){
417 | /*
418 | * menrva
419 | * https://github.com/phadej/menrva
420 | *
421 | * Copyright (c) 2014 Oleg Grenrus
422 | * Licensed under the MIT license.
423 | */
424 |
425 | "use strict";
426 |
427 | var egal = require("./egal.js");
428 | var util = require("./util.js");
429 |
430 | /**
431 | ### Signal
432 |
433 | The core type of menrva. `Signal` is abstract class, and cannot be created explicitly.
434 |
435 | Similar concepts are: *Behaviours* in FRP, *Properties* in bacon.js.
436 |
437 | You can add methods to `Signal`'s prototype. They will be available on all signals.
438 | */
439 | function Signal() {}
440 |
441 | // Each signal has an unique index.
442 | var index = 0;
443 |
444 | function initSignal(signal, value, eq) {
445 | signal.children = [];
446 | signal.callbacks = [];
447 | signal.v = value;
448 |
449 | // `index` is used to implement faster sets of signals
450 | signal.index = index++;
451 |
452 | // `rank` is used to sort signals topologically
453 | signal.rank = signal.calculateRank();
454 |
455 | // `eq` is an equality decision function on the signal values
456 | signal.eq = eq || egal;
457 | }
458 |
459 | function CombinedSignal(parents, f, eq) {
460 | this.parents = parents;
461 | this.f = f;
462 | var value = this.calculate();
463 | initSignal(this, value, eq);
464 | }
465 |
466 | CombinedSignal.prototype = new Signal();
467 |
468 | // rank of combined signal is 1 + maximum rank of parents
469 | CombinedSignal.prototype.calculateRank = function () {
470 | return Math.max.apply(Math, util.pluck(this.parents, "rank")) + 1;
471 | };
472 |
473 | CombinedSignal.prototype.calculate = function () {
474 | return this.f.call(undefined, util.pluck(this.parents, "v"));
475 | };
476 |
477 | /**
478 | #### signal.map
479 |
480 | > map (@ : Signal a, f : a -> b, eq = egal : b -> b -> boolean) : Signal b
481 | */
482 | Signal.prototype.map = function(f, eq) {
483 | var mapped = new CombinedSignal([this], function (xs) {
484 | return f(xs[0]);
485 | }, eq);
486 | this.children.push(mapped);
487 | return mapped;
488 | };
489 |
490 | /**
491 | #### signal.onValue
492 |
493 | > onValue (@ : Signal a, callback : a -> void) -> Unsubscriber
494 |
495 | Add value callback. `callback` is immediately executed with the current value of signal.
496 | After than `callback` will be called, each time signal's value changes.
497 |
498 | The return value is a function, which will remove the callback if executed.
499 | */
500 | Signal.prototype.onValue = function (callback) {
501 | // we wrap callback in function, to make it unique
502 | var wrapped = function (x) { callback(x); };
503 |
504 | // add to callbacks list
505 | this.callbacks.push(wrapped);
506 |
507 | // execute the callback *synchronously*
508 | callback(this.v);
509 |
510 | // return unsubscriber
511 | var that = this;
512 | return function () {
513 | var index = that.callbacks.indexOf(wrapped);
514 | if (index !== -1) {
515 | that.callbacks.splice(index, 1);
516 | }
517 | };
518 | };
519 |
520 | /**
521 | #### signal.value()
522 |
523 | > value (@ : Signal a): Signal a
524 |
525 | Returns the current value of signal.
526 | */
527 | Signal.prototype.value = function() {
528 | return this.v;
529 | };
530 |
531 | /**
532 | ### Source
533 |
534 | A signal which value you can set.
535 |
536 | Similar concepts are: *Bacon.Model* in bacon.js, *BehaviourSubject* in Rx.
537 |
538 |
539 | #### source
540 |
541 | > source (initialValue : a, eq = egal : a -> a -> boolean) : Source a
542 | */
543 | function Source(initialValue, eq) {
544 | initSignal(this, initialValue, eq);
545 | }
546 |
547 | function source(initialValue, eq) {
548 | return new Source(initialValue, eq);
549 | }
550 |
551 | Source.prototype = new Signal();
552 |
553 | Source.prototype.calculateRank = function () {
554 | return 0;
555 | };
556 |
557 | /**
558 | #### source.set
559 |
560 | > set (@ : Source a, tx : Transaction, value : a) : void
561 | */
562 | Source.prototype.set = function (transaction, value) {
563 | transaction.addAction({
564 | type: "set",
565 | signal: this,
566 | value: value,
567 | });
568 | };
569 |
570 | /**
571 | #### source.modify
572 |
573 | > modify (@ : Source a, tx : Transaction, f : a -> a) : void
574 |
575 | Mofify source value. `f` will be called with current value of signal inside the transaction.
576 | */
577 | Source.prototype.modify = function (transaction, f) {
578 | transaction.addAction({
579 | type: "modify",
580 | signal: this,
581 | f: f,
582 | });
583 | };
584 |
585 |
586 | /**
587 | ### Signal combinators
588 | */
589 |
590 | /**
591 | #### combine
592 |
593 | > combine (Signal a..., f : a... -> b) : Signal b
594 |
595 | Applicative n-ary lift. Lift pure function to operate on signals:
596 | ```js
597 | var $sum = menrva.combine($a, $b, function (a, b) {
598 | return a + b;
599 | });
600 | ```
601 | */
602 | function combine() {
603 | var signals = Array.prototype.slice.call(arguments, 0, -1);
604 | var f = arguments[arguments.length - 1];
605 |
606 | var mapped = new CombinedSignal(signals, function (values) {
607 | return f.apply(undefined, values);
608 | });
609 |
610 | // connect to parent
611 | signals.forEach(function (parent) {
612 | parent.children.push(mapped);
613 | });
614 |
615 | return mapped;
616 | }
617 |
618 | module.exports = {
619 | Signal: Signal,
620 | Source: Source,
621 | source: source,
622 | combine: combine,
623 | initSignal: initSignal,
624 | // Internal:
625 | CombinedSignal: CombinedSignal,
626 | };
627 |
628 | },{"./egal.js":2,"./util.js":8}],7:[function(require,module,exports){
629 | /*
630 | * menrva
631 | * https://github.com/phadej/menrva
632 | *
633 | * Copyright (c) 2014 Oleg Grenrus
634 | * Licensed under the MIT license.
635 | */
636 |
637 | "use strict";
638 |
639 | var util = require("./util.js");
640 |
641 | /**
642 | ### Transaction
643 |
644 | One gathers atomic updates into single transaction (to avoid glitches).
645 |
646 | ```js
647 | var tx = menrva.transaction();
648 | sourceA.set(tx, 42);
649 | sourceB.modify(tx, function (x) { return x + x; });
650 | tx.commit(); // not necessary, transactions are auto-commited
651 | ```
652 |
653 | There are also optional syntaxes for simple transactions:
654 | ```js
655 | menrva.transaction()
656 | .set(sourceA, 42)
657 | .modify(sourceB, function (x) { return x + x; })
658 | .commit();
659 | ```
660 | or even
661 | ```js
662 | menrva.transaction([sourceA, 42, sourceB, function(x) { return x + x; }]).commit();
663 | ```
664 | */
665 | function Transaction() {
666 | this.actions = [];
667 | this.commitScheduled = false;
668 | }
669 |
670 | /**
671 | #### transaction
672 |
673 | > transaction (facts) : Transaction
674 |
675 | Create transaction.
676 |
677 | Shorthand syntax:
678 |
679 | > transaction ([sourceA, valueA, sourceB, valueB ...]) : Transaction
680 |
681 | If `value` is function, `source.modify(tx, value)` is called; otherwise `source.set(tx, value)`.
682 | */
683 | function transaction(facts) {
684 | var tx = new Transaction();
685 |
686 | if (Array.isArray(facts)) {
687 | var len = facts.length;
688 | for (var i = 0; i < len; i += 2) {
689 | var source = facts[i];
690 | var value = facts[i + 1];
691 | if (typeof value === "function") {
692 | source.modify(tx, value);
693 | } else {
694 | source.set(tx, value);
695 | }
696 | }
697 | }
698 |
699 | return tx;
700 | }
701 |
702 | /**
703 | #### transaction.commit
704 |
705 | Commit the transaction, forcing synchronous data propagation.
706 | */
707 |
708 | function calculateUpdates(actions) {
709 | var updates = {};
710 | var len = actions.length;
711 | for (var i = 0; i < len; i++) {
712 | var action = actions[i];
713 | // find update fact for signal
714 | var update = updates[action.signal.index];
715 |
716 | // if not update found, create new for action's signal
717 | if (!update) {
718 | update = {
719 | signal: action.signal,
720 | value: action.signal.v,
721 | };
722 | updates[action.signal.index] = update;
723 | }
724 |
725 | // perform action
726 | switch (action.type) {
727 | case "set":
728 | update.value = action.value;
729 | break;
730 | case "modify":
731 | update.value = action.f(update.value);
732 | break;
733 | }
734 | }
735 |
736 | return util.values(updates);
737 | }
738 |
739 | function initialSet(updates) {
740 | var updated = [];
741 | var len = updates.length;
742 | for (var i = 0; i < len; i++) {
743 | var update = updates[i];
744 | // if different value
745 | if (!update.signal.eq(update.signal.v, update.value)) {
746 | // set it
747 | update.signal.v = update.value;
748 |
749 | // collect updated source signal
750 | updated.push(update.signal);
751 | }
752 | }
753 | return updated;
754 | }
755 |
756 | function triggerOnValue(updated) {
757 | var len = updated.length;
758 | for (var i = 0; i < len; i++) {
759 | var updatedSignal = updated[i];
760 | var value = updatedSignal.v;
761 | var callbacks = updatedSignal.callbacks;
762 | var callbacksLen = callbacks.length;
763 | for (var j = 0; j < callbacksLen; j++) {
764 | callbacks[j](value);
765 | }
766 | }
767 | }
768 |
769 | Transaction.prototype.commit = function () {
770 |
771 | // clear timeout
772 | if (this.commitScheduled) {
773 | clearTimeout(this.commitScheduled);
774 | this.commitScheduled = false;
775 | }
776 |
777 | // If nothing to do, short circuit
778 | if (this.actions.length === 0) {
779 | return;
780 | }
781 |
782 | // Data flow
783 |
784 | // traverse actions to aquire new values
785 | var updates = calculateUpdates(this.actions);
786 |
787 | // Apply updates, and collect updated signals
788 | var updated = initialSet(updates);
789 |
790 | // seed propagation push-pull propagation with children of updated sources
791 | var signals = {};
792 | updated.forEach(function (update) {
793 | update.children.forEach(function (child) {
794 | signals[child.index] = child;
795 | });
796 | });
797 |
798 | // until there aren't any signals
799 | while (!util.objIsEmpty(signals)) {
800 | // minimum rank
801 | var rank = Infinity;
802 | for (var rankK in signals) {
803 | rank = Math.min(rank, signals[rankK].rank);
804 | }
805 |
806 | var next = [];
807 | var curr = [];
808 |
809 | for (var k in signals) {
810 | var signal = signals[k];
811 | // skip signals of different (larger!) rank
812 | if (signal.rank !== rank) {
813 | continue;
814 | }
815 |
816 | // new value
817 | var value = signal.calculate();
818 |
819 | // if value is changed
820 | if (!signal.eq(signal.v, value)) {
821 | // set the value
822 | signal.v = value;
823 |
824 | // add signal to updated list
825 | updated.push(signal);
826 |
827 | // add children of updated signal to list of traversable signals
828 | var childrenlen = signal.children.length;
829 | for (var childIdx = 0; childIdx < childrenlen; childIdx++) {
830 | var child = signal.children[childIdx];
831 | next.push(child);
832 | }
833 | }
834 |
835 | // we are done with this signal
836 | curr.push(signal.index);
837 | }
838 |
839 | // Remove traversed
840 | var currLen = curr.length;
841 | for (var currIdx = 0; currIdx < currLen; currIdx++) {
842 | delete signals[curr[currIdx]];
843 | }
844 |
845 | // add next
846 | var nextLen = next.length;
847 | for (var nextIdx = 0; nextIdx < nextLen; nextIdx++) {
848 | signals[next[nextIdx].index] = next[nextIdx];
849 | }
850 | }
851 |
852 | // Trigger onValue callbacks
853 | triggerOnValue(updated);
854 |
855 | // rest cleanupg
856 | this.actions = [];
857 | };
858 |
859 | /**
860 | #### transaction.rollback
861 |
862 | Rollback the transaction. Maybe be called multiple times (consecutives calls are no-op).
863 |
864 | *Note: for now `rollback` only resets the pending actions in transactions. Transaction is still valid, and more actions can be added*
865 | */
866 | Transaction.prototype.rollback = function() {
867 | if (this.commitScheduled) {
868 | clearTimeout(this.commitScheduled);
869 | }
870 | this.actions = [];
871 | this.commitScheduled = false;
872 | };
873 |
874 | Transaction.prototype.deferCommit = function () {
875 | if (!this.commitScheduled) {
876 | var tx = this;
877 | this.commitScheduled = setTimeout(function () {
878 | tx.commit();
879 | });
880 | }
881 | };
882 |
883 | Transaction.prototype.addAction = function (action) {
884 | this.actions.push(action);
885 | this.deferCommit();
886 | };
887 |
888 | Transaction.prototype.set = function (source, value) {
889 | source.set(this, value);
890 | return this;
891 | };
892 |
893 | Transaction.prototype.modify = function (source, f) {
894 | source.modify(this, f);
895 | return this;
896 | };
897 |
898 | module.exports = transaction;
899 |
900 | },{"./util.js":8}],8:[function(require,module,exports){
901 | /*
902 | * menrva
903 | * https://github.com/phadej/menrva
904 | *
905 | * Copyright (c) 2014 Oleg Grenrus
906 | * Licensed under the MIT license.
907 | */
908 |
909 | "use strict";
910 |
911 | function identity(x) {
912 | return x;
913 | }
914 |
915 | function pluck(arr, property) {
916 | var len = arr.length;
917 | var res = new Array(len);
918 | for (var i = 0; i < len; i++) {
919 | res[i] = arr[i][property];
920 | }
921 | return res;
922 | }
923 |
924 | function values(obj) {
925 | var arr = [];
926 | for (var k in obj) {
927 | arr.push(obj[k]);
928 | }
929 | return arr;
930 | }
931 |
932 | function objIsEmpty(obj) {
933 | /* jshint unused:false */
934 | for (var k in obj) {
935 | return false;
936 | }
937 | return true;
938 | }
939 |
940 | function getPath(obj, path) {
941 | var len = path.length;
942 | for (var i = 0; i < len; i++) {
943 | if (obj === undefined || obj === null) {
944 | return obj;
945 | } else {
946 | obj = obj[path[i]];
947 | }
948 | }
949 | return obj;
950 | }
951 |
952 | function setProperty(obj, property, value) {
953 | var copy = {};
954 | for (var k in obj) {
955 | copy[k] = obj[k];
956 | }
957 | copy[property] = value;
958 | return copy;
959 | }
960 |
961 | function setPath(obj, path, value) {
962 | var len = path.length;
963 | var acc = value;
964 | for (var i = len; i > 0; i--) {
965 | var next = getPath(obj, path.slice(0, i - 1));
966 | acc = setProperty(next, path[i - 1], acc);
967 | }
968 | return acc;
969 | }
970 |
971 | function modifyPath(obj, path, f) {
972 | return setPath(obj, path, f(getPath(obj, path)));
973 | }
974 |
975 | module.exports = {
976 | identity: identity,
977 | pluck: pluck,
978 | values: values,
979 | objIsEmpty: objIsEmpty,
980 | getPath: getPath,
981 | setPath: setPath,
982 | modifyPath: modifyPath,
983 | };
984 |
985 | },{}]},{},[4])(4)
986 | });
--------------------------------------------------------------------------------