Comparing version 1.5.2 to 1.6.0
##Changelog | ||
### 1.6 | ||
- feat: `Scan` and `*.scan` added | ||
- feat: Actions mounted on domain have additional property originalLength to indicate arguments amount of original action function | ||
#### On the road to have Immutable.js as option | ||
- fix: `Data` no longer *magically* transforms initial data provided in constructor to Immutable.js data structure | ||
- fix: `View` is merging different streams using plain objects instead of Immutable.js map | ||
### 1.5.2 | ||
@@ -15,4 +24,4 @@ | ||
- feat: streams are sorted so nodes are waiting with their updates for their dependencies are resolved | ||
- feat: `Debounce` and *.debounce() added | ||
- feat: `Throttle` and *.throttle() added | ||
- feat: `Debounce` and `*.debounce()` added | ||
- feat: `Throttle` and `*.throttle()` added | ||
- feat: `Dispatcher` is exposed now | ||
@@ -19,0 +28,0 @@ - feat: `dispatch` function is directly exposed by package for convenience |
@@ -1,2 +0,2 @@ | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("immutable")):"function"==typeof define&&define.amd?define(["immutable"],e):"object"==typeof exports?exports.immview=e(require("immutable")):t.immview=e(t.immutable)}(this,function(t){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return t[r].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0}),e.dispatch=e.Dispatcher=e.Throttle=e.Debounce=e.Domain=e.View=e.Data=void 0;var o=n(1),i=r(o),u=n(3),s=r(u),c=n(8),a=r(c),f=n(9),l=r(f),d=n(10),h=r(d),p=n(2),m=r(p),v=m.default.dispatch;e.Data=s.default,e.View=a.default,e.Domain=i.default,e.Debounce=l.default,e.Throttle=h.default,e.Dispatcher=m.default,e.dispatch=v},function(t,e,n){"use strict";function r(t,e){if(!t.subscribe)throw Error(u+" stream source required");this.stream=t,this._claimActions(e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=r;var o=n(2),i=function(){return null},u="Immview::Domain: ";r.prototype={_claimActions:function(t){var e=this;this._actionNames=t?Object.keys(t):[],this._actionNames.forEach(function(n){if(e[n])throw Error(""+u+n+" is reserved for Domain interface");if("function"!=typeof t[n])throw Error(""+u+n+" action is not a function");e[n]=function(){for(var r=arguments.length,i=Array(r),u=0;r>u;u++)i[u]=arguments[u];(0,o.dispatchDomainAction)(t[n],e,i,1)}})},read:function(){return this.stream.read()},map:function(t){return this.stream.map(t)},debounce:function(t){return this.stream.debounce(t)},throttle:function(t){return this.stream.throttle(t)},subscribe:function(t){return this.stream.subscribe(t)},appendReactor:function(t){return this.stream.appendReactor(t)},destroy:function(){var t=this;this.stream.destroy(),this.stream=null,(0,o.rejectContext)(this),this._actionNames.forEach(function(e){t[e]=i}),this._actionNames=null}}},function(t,e){"use strict";function n(t){return t&&"undefined"!=typeof Symbol&&t.constructor===Symbol?"symbol":typeof t}function r(){for(var t=-1,e=null,n=0;v.length>n;n++){var r=v[n].priority;r>t&&(e=v[n],t=r)}return e}function o(){var t=r();return v.splice(v.indexOf(t),1),t}function i(){var t=o();if(t){var e=t.context,n=t.action,r=t.args;n&&n.apply(e,r)}}function u(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:b;v.push({priority:r,action:t,context:e,args:n})}function s(){m||v.length>0&&(m=!0,O.tick(function(){try{i()}catch(t){c(t)}m=!1,s()}))}function c(t){O.logger.error(g+"Error occured while running a function"),"object"===(void 0===t?"undefined":n(t))?t.stack?O.logger.error(t.stack):O.logger.error(t.name,t.message):O.logger.error(t)}function a(t,e,n,r){u(t,e,n,r),s()}function f(t,e,n){a(t,e,n,y)}function l(t,e,n){a(t,e,n,D)}function d(t,e,n){a(t,e,n,_)}function h(t,e,n){a(t,e,n,b)}function p(t){v=v.filter(function(e){return e.context!==t})}Object.defineProperty(e,"__esModule",{value:!0}),e.dispatch=a,e.dispatchDomainAction=f,e.dispatchDataWrite=l,e.dispatchDataConsume=d,e.dispatchExternal=h,e.rejectContext=p;var m=!1,v=[],g="Immview::Dispatcher: ",b=0,y=1,D=2,_=3,O=e.Dispatcher={dispatch:h,tick:function(t){t()},rejectContext:p,logger:console};e.default=O},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t){a.default.call(this),this.linkTo(null),this.digest((0,u.fromJS)(t))}var i=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t};Object.defineProperty(e,"__esModule",{value:!0}),e.default=o;var u=n(4),s=n(2),c=n(5),a=r(c);o.prototype=i({},a.default.prototype,{write:function(t){var e=this;(0,s.dispatchDataWrite)(function(){e.digest("function"==typeof t?t(e.read()):t)})}})},function(e,n){e.exports=t},function(t,e,n){"use strict";function r(t){return void 0!==t&&null!==t}function o(t,e){return r(e)&&(!r(t)||!(0,a.is)(e,t))}function i(t,e){var n=(0,f.createSchedule)(t);return(0,f.copyQueueOntoSchedule)(e,n)}function u(){(0,f.scheduleLength)(h)>0&&(h=(0,f.runScheduledPriorityJob)(h),(0,l.dispatchDataWrite)(u))}function s(t){return t}function c(){this.reactors=(0,a.Set)()}Object.defineProperty(e,"__esModule",{value:!0}),e.default=c;var a=n(4),f=n(6),l=n(2),d=[],h=[];c.prototype={read:function(){return this.structure},linkTo:function(t){d.push([t&&(t.stream?t.stream:t),this]),h=i(d,h)},unlink:function(){var t=this;d=d.filter(function(e){return e[0]!==t&&e[1]!==t}),h=i(d,h)},consume:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:s;(0,l.dispatchDataConsume)(function(){h=(0,f.scheduleJob)(e,function(){return e.digest(n(t))},h),(0,l.dispatchDataWrite)(u)})},digest:function(t){o(this.structure,t)&&(this.structure=t,this.flush())},appendReactor:function(t){var e=this;return this.reactors=this.reactors.add(t),function(){e.reactors=e.reactors.delete(t)}},subscribe:function(t){return t(this.read()),this.appendReactor(t)},flush:function(){var t=this;this.reactors.forEach(function(e){return e(t.read())})},destroy:function(){this.unlink(),this.structure=null,this.reactors=(0,a.Set)()},map:function(t){var e=n(8).default;return new e(this,t)},debounce:function(t){var e=n(9).default;return new e(this,t)},throttle:function(t){var e=n(10).default;return new e(this,t)}}},function(t,e,n){"use strict";function r(t){return f(t).reduce(function(t,e){return t.unshift([e,null]),t},[])}function o(t,e,n){for(var r=[],o=0;n.length>o;o++){var i=n[o],u=i[0];r.push(u===t?[u,e]:i)}return r}function i(t){for(var e=0,n=0;t.length>n;n++)e+=0|!!t[n][1];return e}function u(t,e){for(var n=0;t.length>n;n++)if(t[n][0]===e)return t[n][1];return null}function s(t,e){for(var n=[].concat(e),r=0;t.length>r;r++){var o=t[r],i=o[0],u=o[1];if(u)for(var s=0;n.length>s;s++){var c=n[s],a=c[0];a===i&&(n[s]=o)}}return n}function c(t){for(var e=null,n=[].concat(t),r=0;t.length>r;r++){var o=t[r],i=o[0],u=o[1];if(u){e=u,n[r]=[i,null];break}}return e&&e(),n}Object.defineProperty(e,"__esModule",{value:!0}),e.sortStreamGraph=void 0,e.createSchedule=r,e.scheduleJob=o,e.scheduleLength=i,e.findJob=u,e.copyQueueOntoSchedule=s,e.runScheduledPriorityJob=c;var a=n(7),f=e.sortStreamGraph=a.getOrder},function(t,e){"use strict";function n(t){return t.reduce(function(t,e){return t.indexOf(e[0])<0&&t.push(e[0]),t.indexOf(e[1])<0&&t.push(e[1]),t},[])}function r(t){var e=[],r=[];return n(t).forEach(function(n){if(!e||e.indexOf(n)<0){var i=o(t,n,r,e);e=i.visited,r=i.stack}}),r}function o(t,e,n,r){var u=r.concat([e]),s=n;return i(t,e).forEach(function(e){if(!u||u.indexOf(e)<0){var n=o(t,e,s,u);u=n.visited,s=n.stack}}),{visited:u,stack:s.concat([e])}}function i(t,e){return t.filter(function(t){return t[0]===e}).map(function(t){return t[1]})}Object.defineProperty(e,"__esModule",{value:!0}),e.getGraphNodes=n,e.getOrder=r,e.visit=o,e.getNodeChildren=i},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t){return t&&"undefined"!=typeof Symbol&&t.constructor===Symbol?"symbol":typeof t}function i(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:f;a.default.call(this),t&&"object"===(void 0===t?"undefined":o(t))&&(t.subscribe?this.connectToSource(t,e):this.connectToMultipleSources(t,e))}var u=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t};Object.defineProperty(e,"__esModule",{value:!0}),e.default=i;var s=n(4),c=n(5),a=r(c),f=function(t){return t};i.prototype=u({},a.default.prototype,{connectToSource:function(t,e){var n=this;this.linkTo(t),this.digest(e(t.read())),this.unsubs=[t.appendReactor(function(t){return n.consume(t,e)})]},connectToMultipleSources:function(t,e){var n=this,r=(0,s.Map)(),o=Object.keys(t);o.forEach(function(e){var o=t[e];n.linkTo(o);var i=o.read();r=r.set(e,i)}),this.unsubs=o.map(function(o){return t[o].appendReactor(function(t){r=r.set(o,t),n.consume(r,e)})}),this.digest(e(r))},destroy:function(){a.default.prototype.destroy.call(this),this.unsubs&&this.unsubs.forEach(function(t){return t()}),this.unsubs=null}})},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;if(s.default.call(this),!t||!t.subscribe)throw Error(c+"incorrect source");this.linkTo(t),this.timeoutID=null,this.digest(t.read()),this.subscription=t.appendReactor(function(t){e.timeoutID&&clearTimeout(e.timeoutID),e.timeoutID=setTimeout(function(){e.timeoutID=null,e.consume(t)},n)})}var i=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t};Object.defineProperty(e,"__esModule",{value:!0}),e.default=o;var u=n(5),s=r(u),c="Immview::Debounce: ";o.prototype=i({},s.default.prototype,{destroy:function(){this.timeoutID&&(clearTimeout(this.timeoutID),this.timeoutID=null),this.subscription&&this.subscription(),s.default.prototype.destroy.call(this)}})},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;if(s.default.call(this),!t||!t.subscribe)throw Error(c+"incorrect source");this.linkTo(t),this.timeoutID=null,this.timeoutedData=null,this.digest(t.read()),this.subscription=t.appendReactor(function(t){e.timeoutedData=t,e.timeoutID||(e.timeoutID=setTimeout(function(){e.timeoutID=null,e.consume(e.timeoutedData)},n))})}var i=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t};Object.defineProperty(e,"__esModule",{value:!0}),e.default=o;var u=n(5),s=r(u),c="Immview::Throttle: ";o.prototype=i({},s.default.prototype,{destroy:function(){this.timeoutID&&(clearTimeout(this.timeoutID),this.timeoutID=null),this.subscription&&this.subscription(),s.default.prototype.destroy.call(this)}})}])}); | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("immutable")):"function"==typeof define&&define.amd?define(["immutable"],e):"object"==typeof exports?exports.immview=e(require("immutable")):t.immview=e(t.immutable)}(this,function(t){return function(t){function e(r){if(n[r])return n[r].exports;var u=n[r]={exports:{},id:r,loaded:!1};return t[r].call(u.exports,u,u.exports,e),u.loaded=!0,u.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0}),e.dispatch=e.Dispatcher=e.Scan=e.Throttle=e.Debounce=e.Domain=e.View=e.Data=void 0;var u=n(1),i=r(u),o=n(3),s=r(o),c=n(9),a=r(c),f=n(11),l=r(f),d=n(12),h=r(d),p=n(13),m=r(p),v=n(2);e.Data=s.default,e.View=a.default,e.Domain=i.default,e.Debounce=l.default,e.Throttle=h.default,e.Scan=m.default,e.Dispatcher=v.Dispatcher,e.dispatch=v.dispatch},function(t,e,n){"use strict";function r(t,e){if(!t.subscribe)throw Error(s+" stream source required");this.stream=t,u(this,e)}function u(t,e){if(!e)return void(t.actions=function(){return[]});var n=Object.keys(e);n.forEach(function(n){var r=e[n];if(void 0!==t[n])throw Error(""+s+n+" is reserved for Domain interface");if("function"!=typeof r)throw Error(""+s+n+" action is not a function");t[n]=function(){for(var e=arguments.length,n=Array(e),u=0;e>u;u++)n[u]=arguments[u];(0,i.dispatchDomainAction)(r,t,n)},t[n].originalLength=r.length||r.originalLength}),t._actionNames=n,t.actions=function(){return n}}Object.defineProperty(e,"__esModule",{value:!0}),e.default=r;var i=n(2),o=function(){return null},s="Immview::Domain: ";r.prototype={read:function(){return this.stream.read()},map:function(t){return this.stream.map(t)},debounce:function(t){return this.stream.debounce(t)},throttle:function(t){return this.stream.throttle(t)},scan:function(t,e){return this.stream.scan(t,e)},subscribe:function(t){return this.stream.subscribe(t)},appendReactor:function(t){return this.stream.appendReactor(t)},destroy:function(){var t=this;this.stream.destroy(),this.stream=null,(0,i.rejectContext)(this),this._actionNames.forEach(function(e){t[e]=o}),this._actionNames=null}}},function(t,e){"use strict";function n(t){return t&&"undefined"!=typeof Symbol&&t.constructor===Symbol?"symbol":typeof t}function r(){var t=u();if(t){var e=t.context,n=t.action,r=t.args;n&&n.apply(e,r)}}function u(){var t=i();return v.splice(v.indexOf(t),1),t}function i(){for(var t=-1,e=null,n=0;v.length>n;n++){var r=v[n].priority;r>t&&(e=v[n],t=r)}return e}function o(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:y;v.push({priority:r,action:t,context:e,args:n})}function s(t,e,n){l(t,e,n,b)}function c(t,e,n){l(t,e,n,_)}function a(t,e,n){l(t,e,n,O)}function f(t,e,n){l(t,e,n,y)}function l(t,e,n,r){o(t,e,n,r),d()}function d(){m||v.length>0&&(m=!0,D.tick(function(){try{r()}catch(t){h(t)}m=!1,d()}))}function h(t){D.logger.error(g+"Error occured while running a function"),"object"===(void 0===t?"undefined":n(t))?t.stack?D.logger.error(t.stack):D.logger.error(t.name,t.message):D.logger.error(t)}function p(t){v=v.filter(function(e){return e.context!==t})}Object.defineProperty(e,"__esModule",{value:!0});var m=!1,v=[],g="Immview::Dispatcher: ",y=0,b=1,_=2,O=3,D={dispatch:f,tick:function(t){t()},rejectContext:p,logger:console};e.default=D,e.Dispatcher=D,e.dispatch=l,e.dispatchDomainAction=s,e.dispatchDataWrite=c,e.dispatchDataConsume=a,e.dispatchExternal=f,e.rejectContext=p},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function u(t){s.default.call(this),this._linkTo(null),this._digest(t)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=u;var i=n(2),o=n(4),s=r(o);u.prototype=Object.create(s.default.prototype),u.prototype.write=function(t){var e=this;(0,i.dispatchDataWrite)(function(){e._digest("function"==typeof t?t(e.read()):t)})}},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e.default=t,e}function u(){this._reactors=[]}function i(t,e){return o(e)&&(!o(t)||!(0,c.is)(e,t))}function o(t){return void 0!==t&&null!==t}function s(t){return t}Object.defineProperty(e,"__esModule",{value:!0}),e.default=u;var c=n(5),a=n(6),f=r(a);u.prototype={read:function(){return this.structure},_linkTo:function(t){f.link(t&&(t.stream?t.stream:t),this)},_unlink:function(){f.unlink(this)},_consume:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:s;f.queue(this,function(){return e._digest(n(t))})},_digest:function(t){i(this.structure,t)&&(this.structure=t,this._flush(t))},_flush:function(t){this._reactors.forEach(function(e){return e(t)})},appendReactor:function(t){var e=this;return this._reactors.indexOf(t)<0&&this._reactors.push(t),function(){e._reactors=e._reactors.filter(function(e){return e!==t})}},subscribe:function(t){return t(this.read()),this.appendReactor(t)},destroy:function(){this._unlink(),this.structure=null,this._reactors=[]},map:function(t){var e=n(9).default;return new e(this,t)},debounce:function(t){var e=n(11).default;return new e(this,t)},throttle:function(t){var e=n(12).default;return new e(this,t)},scan:function(t,e){var r=n(13).default;return new r(this,t,e)}}},function(e,n){e.exports=t},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e.default=t,e}function u(t,e){a(c().concat([[t,e]]))}function i(t){a(c().filter(function(e){var n=p(e,2),r=n[0],u=n[1];return r!==t&&u!==t}))}function o(t,e){(0,g.dispatchDataConsume)(function(){h(v.scheduleJob(t,e,d())),s()})}function s(){(0,g.dispatchDataWrite)(l)}function c(){return y}function a(t){y=t,h(f(y,d()))}function f(t,e){var n=v.createSchedule(t);return v.copyQueueOntoSchedule(e,n)}function l(){v.scheduleLength(d())>0&&(h(v.runScheduledPriorityJob(d())),(0,g.dispatchDataWrite)(l))}function d(){return b}function h(t){b=t}var p=function(){function t(t,e){var n=[],r=!0,u=!1,i=void 0;try{for(var o,s=t[Symbol.iterator]();!(r=(o=s.next()).done)&&(n.push(o.value),!e||n.length!==e);r=!0);}catch(c){u=!0,i=c}finally{try{!r&&s.return&&s.return()}finally{if(u)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();Object.defineProperty(e,"__esModule",{value:!0}),e.link=u,e.unlink=i,e.queue=o;var m=n(7),v=r(m),g=n(2),y=[],b=[]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e.default=t,e}function u(t){return l.getOrder(t).reduce(function(t,e){return t.unshift([e,null]),t},[])}function i(t,e,n){for(var r=[],u=0;n.length>u;u++){var i=n[u],o=i[0];r.push(o===t?[o,e]:i)}return r}function o(t){for(var e=0,n=0;t.length>n;n++)e+=0|!!t[n][1];return e}function s(t,e){for(var n=0;t.length>n;n++)if(t[n][0]===e)return t[n][1];return null}function c(t,e){for(var n=[].concat(e),r=0;t.length>r;r++){var u=t[r],i=u[0],o=u[1];if(o)for(var s=0;n.length>s;s++){var c=n[s],a=c[0];a===i&&(n[s]=u)}}return n}function a(t){for(var e=null,n=[].concat(t),r=0;t.length>r;r++){var u=t[r],i=u[0],o=u[1];if(o){e=o,n[r]=[i,null];break}}return e&&e(),n}Object.defineProperty(e,"__esModule",{value:!0}),e.createSchedule=u,e.scheduleJob=i,e.scheduleLength=o,e.findJob=s,e.copyQueueOntoSchedule=c,e.runScheduledPriorityJob=a;var f=n(8),l=r(f)},function(t,e){"use strict";function n(t){var e=r(t).reduce(function(e,n){return i(e.visited,n)?e:u(t,n,e)},{visited:[],stack:[]});return e.stack}function r(t){return t.reduce(function(t,e){return t.indexOf(e[0])<0&&t.push(e[0]),t.indexOf(e[1])<0&&t.push(e[1]),t},[])}function u(t,e,n){var r=o(t,e).reduce(function(e,n){return i(e.visited,n)?e:u(t,n,e)},{visited:n.visited.concat([e]),stack:n.stack}),s={visited:r.visited,stack:r.stack.concat([e])};return s}function i(t,e){return t.indexOf(e)>=0}function o(t,e){return t.filter(function(t){return t[0]===e}).map(function(t){return t[1]})}Object.defineProperty(e,"__esModule",{value:!0}),e.getOrder=n,e.getAllNodes=r,e.getOrderStartingFromNode=u,e.getNodeChildren=o},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function u(t){return t&&"undefined"!=typeof Symbol&&t.constructor===Symbol?"symbol":typeof t}function i(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:o;if(d.default.call(this),t&&"object"===(void 0===t?"undefined":u(t)))return void(this.unsubs=t.subscribe?s(this,t,e):c(this,t,e));throw Error(h+"No sources to plug in")}function o(t){return t}function s(t,e,n){return t._linkTo(e),t._digest(n(e.read())),[e.appendReactor(function(e){return t._consume(e,n)})]}function c(t,e,n){var r=new f.default,u=Object.keys(e);u.forEach(function(n){var u=e[n];t._linkTo(u);var i=u.read();r=r.set(n,i)});var i=u.map(function(u){return e[u].appendReactor(function(e){r=r.set(u,e),t._consume(r.clone(),n)})});return t._digest(n(r.clone())),i}Object.defineProperty(e,"__esModule",{value:!0}),e.default=i;var a=n(10),f=r(a),l=n(4),d=r(l),h="Immview::View: ";i.prototype=Object.create(d.default.prototype),i.prototype.destroy=function(){d.default.prototype.destroy.call(this),this.unsubs&&this.unsubs.forEach(function(t){return t()}),this.unsubs=null}},function(t,e){"use strict";function n(t){return Array.isArray(t)?t:Array.from(t)}function r(t){return t&&"undefined"!=typeof Symbol&&t.constructor===Symbol?"symbol":typeof t}function u(t){for(var e in t)Object.prototype.hasOwnProperty.call(t,e)&&(this[e]=t[e])}Object.defineProperty(e,"__esModule",{value:!0}),e.default=u,u.prototype={clone:function(){return new u(this)},set:function(t,e){var n=new u(this);return n[t]=e,n},get:function(t){return this[t]},map:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this,n=new u;for(var r in this)Object.prototype.hasOwnProperty.call(this,r)&&(n[r]=t.call(e,this[r]));return n},toJS:function(){return this.map(function(t){return"object"===(void 0===t?"undefined":r(t))&&"function"==typeof t.toJS?t.toJS():t})},toObject:function(){return this},getIn:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=n(t),r=e[0],u=e.slice(1),i=arguments[1];return u.length?this.get(r).getIn(u,i):void 0===this.get(r)?i:this.get(r)},setIn:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=n(t),r=e[0],i=e.slice(1),o=arguments[1];return i.length?this.set(r,(this.get(r)||new u).setIn(i,o)):this.set(r,o)}}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function u(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;if(o.default.call(this),!t||!t.subscribe)throw Error(s+"incorrect source");this._linkTo(t),this.timeoutID=null,this._digest(t.read()),this.subscription=t.appendReactor(function(t){e.timeoutID&&clearTimeout(e.timeoutID),e.timeoutID=setTimeout(function(){e.timeoutID=null,e._consume(t)},n)})}Object.defineProperty(e,"__esModule",{value:!0}),e.default=u;var i=n(4),o=r(i),s="Immview::Debounce: ";u.prototype=Object.create(o.default.prototype),u.prototype.destroy=function(){this.timeoutID&&(clearTimeout(this.timeoutID),this.timeoutID=null),this.subscription&&this.subscription(),o.default.prototype.destroy.call(this)}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function u(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;if(o.default.call(this),!t||!t.subscribe)throw Error(s+"incorrect source");this._linkTo(t),this.timeoutID=null,this.timeoutedData=null,this._digest(t.read()),this.subscription=t.appendReactor(function(t){e.timeoutedData=t,e.timeoutID||(e.timeoutID=setTimeout(function(){e.timeoutID=null,e._consume(e.timeoutedData)},n))})}Object.defineProperty(e,"__esModule",{value:!0}),e.default=u;var i=n(4),o=r(i),s="Immview::Throttle: ";u.prototype=Object.create(o.default.prototype),u.prototype.destroy=function(){this.timeoutID&&(clearTimeout(this.timeoutID),this.timeoutID=null),this.subscription&&this.subscription(),o.default.prototype.destroy.call(this)}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function u(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:2,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;c.default.apply(this),this._linkTo(t);var u=o(i(r,n),n,t.read());this._digest(u),this.unsubscribe=t.appendReactor(function(t){u=o(u,n,t),e._consume(u)})}function i(t,e){if(null===t)return[];for(var n=[],r=0;e>r;r++)n.push(t);return n}function o(t,e,n){var r=t.slice(-1*e+1);return r.push(n),r}Object.defineProperty(e,"__esModule",{value:!0}),e.default=u;var s=n(4),c=r(s);u.prototype=Object.create(c.default.prototype),u.prototype.read=function(){return[].concat(c.default.prototype.read.apply(this))},u.prototype.destroy=function(){c.default.prototype.destroy.apply(this),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null)}}])}); | ||
//# sourceMappingURL=immview.js.map |
203
docs.md
#Docs | ||
## Data | ||
## Domain | ||
### class | ||
#### ( source: Stream ) | ||
#### ( source: Stream , { [string]: () => void } ) | ||
First argument of constructor is a data source. It can be any observable class listed below. That will be a *state* stream of the newly created **Domain**. Only single data source can be tied to it, and because of that **Domain** can be used as data source. | ||
Second argument is an object aggregating functions used to create commands that will be exposed as part of the **Domain** interface. These functions will be wrapped with an internal Dispatcher calls. **Domain** *actions* won't be returning anything and will be executed in call order, **one after another** even if *actions* calls are done from another *action* call. | ||
```javascript | ||
// example usage | ||
import {Data, View, Domain} from 'immview'; | ||
const EyesDomain = new Domain( | ||
new View({ | ||
HorizonDomain, | ||
MusclesDomain, | ||
}), | ||
{ | ||
roll() { | ||
MusclesDomain.doStuff(); | ||
} | ||
} | ||
); | ||
EyesDomain.roll(); | ||
``` | ||
### Domain::read (since 1.2) | ||
bypassed to state stream given in constructor (see `Data` or `View`) | ||
> Although **Domain** can be created using **Data**, Domain won't be having **write** method available to discourage writing directly from dependent instances. | ||
### Domain::subscribe (since 1.2) | ||
bypassed to state stream given in constructor (see `Data`) | ||
### Domain::map (since 1.4) | ||
bypassed to state stream given in constructor (see `Data`) | ||
### Domain::debounce (since 1.5) | ||
bypassed to state stream given in constructor (see `Data`) | ||
### Domain::throttle (since 1.5) | ||
bypassed to state stream given in constructor (see `Data`) | ||
### Domain::scan (since 1.6) | ||
bypassed to state stream given in constructor (see `Data`) | ||
### Domain::[ACTION_NAME].originalLength (since 1.6) | ||
Amount of arguments indicated by original action function | ||
## Data | ||
### class | ||
#### ( initialData: Immutable ) | ||
```javascript | ||
import {Data} from 'immview'; | ||
new Data( initialData: Immutable ) | ||
new Data( 2 ) | ||
``` | ||
An **initialData** object can be object any Immutable.js data structure. | ||
An **initialData** object can be object any native immutable (bool, string, number) or Immutable.js data structure. | ||
###read() => Immutable | ||
### Data::read | ||
#### () => Immutable | ||
@@ -18,3 +71,3 @@ Method used to retrieve current structure holden by the Data. | ||
```javascript | ||
//Example | ||
// Example | ||
const source = new Data(fromJS({a: 1})); | ||
@@ -24,4 +77,5 @@ source.read().get('a'); // = 1 | ||
### write(change: Immutable ) => void | ||
### write(change: (currentStructure) => Immutable) => void | ||
### Data::write | ||
#### (change: Immutable ) => void | ||
#### (change: (currentStructure) => Immutable) => void | ||
@@ -43,15 +97,28 @@ Method used to store new Immutable data structure. | ||
### subscribe( reaction: (data: Immutable) => void ) => () => void | ||
### Data::subscribe | ||
#### ( reaction: (data: Immutable) => void ) => () => void | ||
Registers a function called every time when the Data changes value that it holds. Returns a function to unregister the subscription. | ||
### map( processor: (data: Immutable) => Immutable ) => View (since 1.4) | ||
Creates new **View** with the **Data** instance as a data source and **processor** as transformer function. **Processor** function will receive Immutable data structure and should be returned Immutable data structure too. | ||
### Data::map | ||
#### ( processor: (data: Immutable) => Immutable ) => View (since 1.4) | ||
Creates new **View** with the current instance as a data source and **processor** as transformer function. **Processor** function will receive Immutable data structure and should be returned Immutable data structure too. | ||
### debounce( timeout: number ) => Debounce (since 1.5) | ||
### Data::debounce | ||
#### ( timeout: number ) => Debounce (since 1.5) | ||
Creates a new stream of values being pushed with a provided delay since last update. | ||
### throttle( timeout: number ) => Throttle (since 1.5) | ||
### Data::throttle | ||
#### ( timeout: number ) => Throttle (since 1.5) | ||
Creates a new stream of values being push with a provided delay since first update. | ||
### Data::scan | ||
#### ( valuesToRemember: number, initialValue: any ) => Scan (since 1.6) | ||
Creates a new stream of `List` of values that were pushed from a source stream recently. List has a max length of `valuesToRemember` argument. Additionally for first `valuesToRemember - 1` runs if `initialValue` is provided, `List` of values is always `valuesToRemember` long and filled with `initialValue` for not yet existing steps. | ||
## View | ||
### class | ||
#### ( sourceDataProvider: Stream ) | ||
#### ( sourceDataProvider: { [string]: Stream } ) | ||
#### ( sourceDataProvider: Stream, processor: (sourceData: Immutable) => Immutable ) | ||
#### ( sourceDataProvider: { [string]: Stream } , processor: (sourceData: { [string]: Immutable }) => Immutable ) | ||
@@ -76,3 +143,4 @@ ```javascript | ||
sourceDataProvider: { [string]: Stream } , | ||
processor: (sourceData: Immutable) => Immutable | ||
processor: | ||
(sourceData: { [string]: Immutable}) => Immutable | ||
) | ||
@@ -84,69 +152,59 @@ ``` | ||
### read() => Immutable | ||
### View::read | ||
Same as `Data` function of the same name. | ||
Method used to retrieve current structure holden by the View. | ||
### View::subscribe | ||
Same as `Data` function of the same name. | ||
### subscribe( reaction: (data) => void ) => () => void | ||
Registers a function called every time when the View changes value that it holds. Returns a function to unregister the subscription. | ||
### View::map (since 1.4) | ||
Same as `Data` function of the same name. | ||
### map( processor: (sourceData: Immutable) => Immutable ) => View (since 1.4) | ||
Creates new View with current instance as a data source and **processor** as transformer function. **Processor** function will receive Immutable data structure and should be returned Immutable data structure too. | ||
### View::debounce (since 1.5) | ||
Same as `Data` function of the same name. | ||
### debounce( timeout: number ) => Debounce (since 1.5) | ||
Creates a new stream of values being pushed with a provided delay since last update. | ||
### View::throttle (since 1.5) | ||
Same as `Data` function of the same name. | ||
### throttle( timeout: number ) => Throttle (since 1.5) | ||
Creates a new stream of values being push with a provided delay since first update. | ||
### View::scan (since 1.6) | ||
Same as `Data` function of the same name. | ||
## Domain | ||
## Debounce (since 1.5) | ||
### class | ||
#### ( sourceStream, delay: number ) | ||
```javascript | ||
new Domain ( source: Stream ) | ||
new Domain ( source: Stream , { [string]: () => void } ) | ||
// or | ||
sourceStream.debounce(delay) | ||
``` | ||
First argument of constructor is a data source. That will be a *state* stream of the newly created **Domain**. Only single data source can be tied to it, so **Domain** can be used as data source. | ||
Creates a new stream of values being pushed with a provided delay since **last** update. | ||
Shares interface with a **View**. | ||
Second argument is an object aggregating functions used to create commands that will be exposed as part of the **Domain** interface. These functions will be wrapped with an internal Dispatcher calls. **Domain** *actions* won't be returning anything and will be executed in call order, **one after another** even if *actions* calls are done from another *action* call. | ||
## Throttle (since 1.5) | ||
### class | ||
#### ( sourceStream, delay: number ) | ||
```javascript | ||
// example usage | ||
import {Data, View, Domain} from 'immview'; | ||
const EyesDomain = new Domain( | ||
new View({ | ||
HorizonDomain, | ||
MusclesDomain, | ||
}), | ||
{ | ||
roll() { | ||
MusclesDomain.doStuff(); | ||
} | ||
} | ||
); | ||
EyesDomain.roll(); | ||
// or | ||
sourceStream.throttle(delay) | ||
``` | ||
### read() => Immutable (since 1.2) | ||
Creates a new stream of values being push with a provided delay since **first** update. | ||
Shares interface with a **View**. | ||
Method used to retrieve current Domain's *state*. | ||
## Scan (since 1.6) | ||
### class | ||
#### ( sourceStream, valuesToRemember: number = 2, initialValue: any = null ) | ||
> Although **Domain** can be created using **Data**, Domain won't be having **write** method available to discourage writing directly from dependent instances. | ||
```javascript | ||
// or | ||
sourceStream.scan(valuesToRemember = 2, initialValue = null) | ||
``` | ||
### subscribe( reaction: (data) => void ) => () => void (since 1.2) | ||
Registers a function called every time when the Domain's *state* changes. Returns a function to unsubscribe from updates. | ||
Creates a new stream of `Arrays` of values that were pushed from a source stream recently. List has a max length of `valuesToRemember` argument. Additionally if `initialValue` is provided for first `valuesToRemember - 1` runs , `Array` of values is always `valuesToRemember` long and filled with `initialValue` for not yet existing steps. | ||
### map( processor: (sourceData: Immutable) => Immutable ) => View (since 1.4) | ||
Creates new View with the Domain instance source as a data source and **processor** as transformer function. **Processor** function will receive Immutable data structure and should be returned Immutable data structure too. | ||
Shares interface with a **View**. | ||
### debounce( timeout: number ) => Debounce (since 1.5) | ||
Creates a new stream of values being pushed with a provided delay since last update. | ||
### throttle( timeout: number ) => Throttle (since 1.5) | ||
Creates a new stream of values being push with a provided delay since first update. | ||
## Debounce, Throttle | ||
These share interface with a **View**. | ||
## Dispatcher | ||
### module | ||
@@ -156,3 +214,6 @@ ### Dispatcher.logger (since 1.5) | ||
Replace to change for logging errors in queue runner | ||
(default: console) | ||
```javascript | ||
// default | ||
Dispatcher.logger = console; | ||
``` | ||
@@ -162,6 +223,6 @@ ### Dispatcher.tick (since 1.5) | ||
Replace to change how next function is being called. | ||
default: | ||
```javascript | ||
// default | ||
Dispatcher.tick = func => func(); | ||
``` | ||
func => func() | ||
``` | ||
@@ -171,5 +232,8 @@ ### Dispatcher.dispatch( action: function, context: any, args: Array<any> ) (since 1.5) | ||
Call to place action on a queue. Can be imported directly from package too. | ||
```javascript | ||
// example | ||
Dispatcher.dispatch( console.log, console, ['oi!'] ); | ||
// 'oi!' | ||
``` | ||
import { dispatch } from 'immview'; | ||
``` | ||
@@ -179,1 +243,10 @@ ### Dispatcher.rejectContext( context: any ) (since 1.5) | ||
Call to remove actions with provided context from queue. | ||
```javascript | ||
// example | ||
Dispatcher.dispatch( () => { | ||
console.log('ay!'); | ||
Dispatcher.dispatch( console.log, console, ['oi!'] ); | ||
Dispatcher.rejectContext( console ); | ||
} ); | ||
// 'ay!' | ||
``` |
@@ -6,3 +6,5 @@ var webpackConfig = require('./webpack.config.js'); | ||
webpackConfig.entry = './tests/index.js'; | ||
webpackConfig.externals = null; | ||
webpackConfig.externals = { | ||
immutable: 'Immutable' | ||
}; | ||
@@ -12,25 +14,20 @@ module.exports = function(config) { | ||
// base path that will be used to resolve all patterns (eg. files, exclude) | ||
basePath: '', | ||
// frameworks to use | ||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter | ||
frameworks: ['jasmine'], | ||
// list of files / patterns to load in the browser | ||
files: (process.env.TRAVIS ? ['node_modules/babel-polyfill/dist/polyfill.min.js'] : []).concat([ | ||
//'node_modules/immutable/dist/immutable.js', | ||
'node_modules/immutable/dist/immutable.js', | ||
]).concat([ | ||
'tests/index.js', | ||
//'src/index.js', | ||
//'tests/*.spec.js', | ||
]), | ||
// list of files to exclude | ||
exclude: [], | ||
// preprocess matching files before serving them to the browser | ||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor | ||
preprocessors: { | ||
'tests/index.js': ['webpack', 'sourcemap', 'coverage'], | ||
'tests/index.js': [ | ||
'webpack', | ||
'sourcemap', | ||
// 'coverage', | ||
], | ||
}, | ||
@@ -47,18 +44,16 @@ | ||
// test results reporter to use | ||
// possible values: 'dots', 'progress' | ||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter | ||
reporters: ['progress', 'coverage'], | ||
reporters: [ | ||
'progress', | ||
// 'coverage', | ||
], | ||
coverageReporter: { | ||
type: 'html', | ||
dir: 'tests', | ||
subdir: 'coverage', | ||
file: 'coverage.html', | ||
}, | ||
// coverageReporter: { | ||
// type: 'html', | ||
// dir: 'tests', | ||
// subdir: 'coverage', | ||
// file: 'coverage.html', | ||
// }, | ||
// web server port | ||
port: 9876, | ||
// enable / disable colors in the output (reporters and logs) | ||
colors: true, | ||
@@ -70,7 +65,4 @@ | ||
// enable / disable watching file and executing tests whenever any file changes | ||
autoWatch: true, | ||
// start these browsers | ||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher | ||
browsers: process.env.TRAVIS ? ['PhantomJS'] : ['Chrome'], | ||
@@ -77,0 +69,0 @@ |
{ | ||
"name": "immview", | ||
"version": "1.5.2", | ||
"version": "1.6.0", | ||
"description": "", | ||
@@ -8,8 +8,9 @@ "main": "dist/immview.js", | ||
"scripts": { | ||
"test": "NODE_ENV=testing $(npm bin)/karma start", | ||
"test_once": "NODE_ENV=testing $(npm bin)/karma start --single-run", | ||
"test_node": "NODE_ENV=testing node test_node.js", | ||
"test:once": "NODE_ENV=testing $(npm bin)/karma start --single-run", | ||
"test:node": "NODE_ENV=testing node test_node.js", | ||
"test:watch": "NODE_ENV=testing $(npm bin)/karma start", | ||
"test": "npm run fix && npm run test:node && npm run test:once", | ||
"start": "NODE_ENV=testing $(npm bin)/webpack --watch", | ||
"build": "NODE_ENV=production $(npm bin)/webpack", | ||
"preversion": "npm run test_node && npm run build && git add -A && git commit -m \"build\";", | ||
"build": "NODE_ENV=production $(npm bin)/webpack && git add -A && git commit -m \"build\";", | ||
"preversion": "npm run test && npm run build", | ||
"fix:src": "$(npm bin)/jscs --fix src/*.js", | ||
@@ -16,0 +17,0 @@ "fix:tests": "$(npm bin)/jscs --fix tests/*.js", |
@@ -1,2 +0,2 @@ | ||
# immview 1.5 | ||
# immview 1.6 | ||
@@ -27,4 +27,5 @@ Immview is a library to create **Domains** - *non-visual components* - similar to flux stores, exposing their **state** through *observables* pushing immutable data structures and having their own, specific to them **actions**. It can be used in place of any flux implementation, redux. | ||
* [Reactive logic with immview](https://medium.com/@arturkulig/reactive-logic-with-immview-cf60ff06b7dc) - an introduction to reasoning behind immview | ||
* [Introduction to Immutable.js (Auth0 Blog)](https://auth0.com/blog/2016/03/23/intro-to-immutable-js/) | ||
* [Documentation](./docs.md) | ||
* [Simple usage](./usage_simple.md) | ||
* [Changelog](./changelog.md) |
@@ -1,5 +0,1 @@ | ||
import { | ||
fromJS, | ||
} from 'immutable'; | ||
import { dispatchDataWrite } from './Dispatcher'; | ||
@@ -11,21 +7,20 @@ import Reactor from './Reactor.js'; | ||
this.linkTo(null); | ||
this.digest(fromJS(initialData)); | ||
this._linkTo(null); | ||
this._digest(initialData); | ||
} | ||
Data.prototype = { | ||
...Reactor.prototype, | ||
/** | ||
* Dispatch a change instruction to the Data | ||
* @param {Iterable|function(data: Iterable):Iterable} change | ||
*/ | ||
write(change) { | ||
dispatchDataWrite(() => { | ||
if (typeof change === 'function') { | ||
this.digest(change(this.read())); | ||
} else { | ||
this.digest(change); | ||
} | ||
}); | ||
}, | ||
Data.prototype = Object.create(Reactor.prototype); | ||
/** | ||
* Dispatch a change instruction to the Data | ||
* @param {Iterable|function(data: Iterable):Iterable} change | ||
*/ | ||
Data.prototype.write = function (change) { | ||
dispatchDataWrite(() => { | ||
if (typeof change === 'function') { | ||
this._digest(change(this.read())); | ||
} else { | ||
this._digest(change); | ||
} | ||
}); | ||
}; |
@@ -12,6 +12,6 @@ import Reactor from './Reactor.js'; | ||
this.linkTo(source); | ||
this._linkTo(source); | ||
this.timeoutID = null; | ||
this.digest(source.read()); | ||
this._digest(source.read()); | ||
this.subscription = source.appendReactor(data => { | ||
@@ -23,3 +23,3 @@ if (this.timeoutID) { | ||
this.timeoutID = null; | ||
this.consume(data); | ||
this._consume(data); | ||
}, timeout); | ||
@@ -29,15 +29,13 @@ }); | ||
Debounce.prototype = { | ||
...Reactor.prototype, | ||
Debounce.prototype = Object.create(Reactor.prototype); | ||
destroy() { | ||
if (this.timeoutID) { | ||
clearTimeout(this.timeoutID); | ||
this.timeoutID = null; | ||
} | ||
if (this.subscription) { | ||
this.subscription(); | ||
} | ||
Reactor.prototype.destroy.call(this); | ||
}, | ||
Debounce.prototype.destroy = function () { | ||
if (this.timeoutID) { | ||
clearTimeout(this.timeoutID); | ||
this.timeoutID = null; | ||
} | ||
if (this.subscription) { | ||
this.subscription(); | ||
} | ||
Reactor.prototype.destroy.call(this); | ||
}; |
@@ -10,24 +10,25 @@ let isRunning = false; | ||
function findMaxPriority() { | ||
let maxPriority = -1; | ||
let firstOfPriority = null; | ||
for (let i = 0; i < queue.length; i++) { | ||
const jobPriority = queue[i].priority; | ||
if (jobPriority > maxPriority) { | ||
firstOfPriority = queue[i]; | ||
maxPriority = jobPriority; | ||
} | ||
} | ||
return firstOfPriority; | ||
} | ||
const Dispatcher = { | ||
dispatch: dispatchExternal, | ||
function shiftFromQueue() { | ||
const toRun = findMaxPriority(); | ||
queue.splice( | ||
queue.indexOf(toRun), | ||
1 | ||
); | ||
return toRun; | ||
tick(func) { | ||
func(); | ||
}, | ||
rejectContext, | ||
logger: console, | ||
}; | ||
export { | ||
Dispatcher as default, | ||
Dispatcher, | ||
dispatch, | ||
dispatchDomainAction, | ||
dispatchDataWrite, | ||
dispatchDataConsume, | ||
dispatchExternal, | ||
rejectContext, | ||
}; | ||
/** | ||
@@ -46,4 +47,26 @@ * Execute first action of current queue | ||
} | ||
}; | ||
} | ||
function shiftFromQueue() { | ||
const toRun = findMaxPriority(); | ||
queue.splice( | ||
queue.indexOf(toRun), | ||
1 | ||
); | ||
return toRun; | ||
} | ||
function findMaxPriority() { | ||
let maxPriority = -1; | ||
let firstOfPriority = null; | ||
for (let i = 0; i < queue.length; i++) { | ||
const jobPriority = queue[i].priority; | ||
if (jobPriority > maxPriority) { | ||
firstOfPriority = queue[i]; | ||
maxPriority = jobPriority; | ||
} | ||
} | ||
return firstOfPriority; | ||
} | ||
/** | ||
@@ -65,3 +88,31 @@ * Append new action onto end of the queue | ||
function dispatchDomainAction(action, context, args) { | ||
dispatch(action, context, args, PRIORITY_DOMAIN); | ||
} | ||
function dispatchDataWrite(action, context, args) { | ||
dispatch(action, context, args, PRIORITY_DATA_WRITE); | ||
} | ||
function dispatchDataConsume(action, context, args) { | ||
dispatch(action, context, args, PRIORITY_DATA_CONSUMING); | ||
} | ||
function dispatchExternal(action, context, args) { | ||
dispatch(action, context, args, PRIORITY_EXT); | ||
} | ||
/** | ||
* Place provided function on a queue and run it as soon as possible | ||
* @param {function} action | ||
* @param {*} [context] | ||
* @param {Array.<*>} [args] | ||
* @param {number} [priority=0] priority for dispatched action. 0, 1, 2 are acceptable | ||
*/ | ||
function dispatch(action, context, args, priority) { | ||
appendToQueue(action, context, args, priority); | ||
startQueue(); | ||
} | ||
/** | ||
* Starts executing the queue | ||
@@ -101,23 +152,2 @@ */ | ||
export function dispatch(action, context, args, priority) { | ||
appendToQueue(action, context, args, priority); | ||
startQueue(); | ||
} | ||
export function dispatchDomainAction(action, context, args) { | ||
dispatch(action, context, args, PRIORITY_DOMAIN); | ||
} | ||
export function dispatchDataWrite(action, context, args) { | ||
dispatch(action, context, args, PRIORITY_DATA_WRITE); | ||
} | ||
export function dispatchDataConsume(action, context, args) { | ||
dispatch(action, context, args, PRIORITY_DATA_CONSUMING); | ||
} | ||
export function dispatchExternal(action, context, args) { | ||
dispatch(action, context, args, PRIORITY_EXT); | ||
} | ||
/** | ||
@@ -127,26 +157,4 @@ * Removes all queued actions tied with a context | ||
*/ | ||
export function rejectContext(context) { | ||
function rejectContext(context) { | ||
queue = queue.filter(item => item.context !== context); | ||
} | ||
export const Dispatcher = { | ||
/** | ||
* Place provided function on a queue and run it as soon as possible | ||
* @param {function} action | ||
* @param {*} [context] | ||
* @param {Array.<*>} [args] | ||
* @param {number} [priority=0] priority for dispatched action. 0, 1, 2 are acceptable | ||
*/ | ||
dispatch: dispatchExternal, | ||
tick(func) { | ||
func(); | ||
}, | ||
rejectContext, | ||
logger: console, | ||
}; | ||
export default Dispatcher; |
@@ -17,8 +17,7 @@ import { | ||
export default function Domain(stream, actions) { | ||
if (stream.subscribe) { | ||
this.stream = stream; | ||
} else { | ||
if (!stream.subscribe) { | ||
throw new Error(`${errorPrefix} stream source required`); | ||
} | ||
this._claimActions(actions); | ||
this.stream = stream; | ||
assignActions(this, actions); | ||
} | ||
@@ -28,26 +27,2 @@ | ||
/** | ||
* @private | ||
*/ | ||
_claimActions(actions) { | ||
/** | ||
* @private | ||
*/ | ||
this._actionNames = actions ? Object.keys(actions) : []; | ||
this._actionNames.forEach((actionName) => { | ||
if (this[actionName]) { | ||
throw new Error(`${errorPrefix}${actionName} is reserved for Domain interface`); | ||
} | ||
if (typeof actions[actionName] !== 'function') { | ||
throw new Error(`${errorPrefix}${actionName} action is not a function`); | ||
} | ||
this[actionName] = (...args) => { | ||
dispatchDomainAction(actions[actionName], this, args, 1); | ||
}; | ||
}); | ||
}, | ||
/** | ||
* Retrieve last value on stream attached to the Domain | ||
@@ -98,2 +73,6 @@ * @returns {Iterable} | ||
scan(valuesToRemember, initialValue) { | ||
return this.stream.scan(valuesToRemember, initialValue); | ||
}, | ||
/** | ||
@@ -138,1 +117,30 @@ * Register a listener to changes on data stream. | ||
}; | ||
function assignActions(ctx, actions) { | ||
if (!actions) { | ||
ctx.actions = () => []; | ||
return; | ||
} | ||
const actionNames = Object.keys(actions); | ||
actionNames.forEach((actionName) => { | ||
const action = actions[actionName]; | ||
if (ctx[actionName] !== undefined) { | ||
throw new Error(`${errorPrefix}${actionName} is reserved for Domain interface`); | ||
} | ||
if (typeof action !== 'function') { | ||
throw new Error(`${errorPrefix}${actionName} action is not a function`); | ||
} | ||
ctx[actionName] = (...args) => { | ||
dispatchDomainAction(action, ctx, args); | ||
}; | ||
ctx[actionName].originalLength = action.length || action.originalLength; | ||
}); | ||
ctx._actionNames = actionNames; //legacy TODO remove | ||
ctx.actions = () => actionNames; | ||
} |
@@ -6,6 +6,8 @@ import Domain from './Domain.js'; | ||
import Throttle from './Throttle.js'; | ||
import Dispatcher from './Dispatcher'; | ||
import Scan from './Scan.js'; | ||
import { | ||
Dispatcher, | ||
dispatch, | ||
} from './Dispatcher'; | ||
const { dispatch } = Dispatcher; | ||
export { | ||
@@ -17,4 +19,5 @@ Data, | ||
Throttle, | ||
Scan, | ||
Dispatcher, | ||
dispatch, | ||
}; |
import { | ||
Set, | ||
is, | ||
} from 'immutable'; | ||
import { | ||
scheduleLength, | ||
scheduleJob, | ||
runScheduledPriorityJob, | ||
createSchedule, | ||
copyQueueOntoSchedule, | ||
} from './StreamSchedule'; | ||
import * as Digest from './Digest'; | ||
import { | ||
dispatchDataWrite, | ||
dispatchDataConsume, | ||
} from './Dispatcher'; | ||
function hasValue(v) { | ||
return ( | ||
v !== undefined && | ||
v !== null | ||
); | ||
} | ||
function shouldStructureBeReplaced(structure, candidate) { | ||
return ( | ||
hasValue(candidate) && ( | ||
!hasValue(structure) || | ||
!is(candidate, structure) | ||
) | ||
); | ||
}; | ||
let digestEdges = []; | ||
let digestSchedule = []; | ||
function restoreNodeJobs(edges, currentSchedule) { | ||
const digestJobMap = createSchedule(edges); | ||
return copyQueueOntoSchedule(currentSchedule, digestJobMap); | ||
} | ||
function doNextJob() { | ||
if (scheduleLength(digestSchedule) > 0) { | ||
digestSchedule = runScheduledPriorityJob(digestSchedule); | ||
dispatchDataWrite(doNextJob); | ||
} | ||
} | ||
function identity(v) { | ||
return v; | ||
} | ||
/** | ||
* Reactor is an base class for all immview observables | ||
* @constructor | ||
*/ | ||
export default function Reactor() { | ||
/** | ||
* @private | ||
* @type {Function[]} | ||
*/ | ||
this.reactors = Set(); | ||
this._reactors = []; | ||
} | ||
@@ -66,26 +24,43 @@ | ||
linkTo(sourceNode) { | ||
digestEdges.push([ | ||
/** | ||
* Registers new connection between new observables | ||
* @protected | ||
* @param {Reactor|Domain} sourceNode | ||
*/ | ||
_linkTo(sourceNode) { | ||
Digest.link( | ||
sourceNode && (sourceNode.stream ? sourceNode.stream : sourceNode), | ||
this, | ||
]); | ||
digestSchedule = restoreNodeJobs(digestEdges, digestSchedule); | ||
this | ||
); | ||
}, | ||
unlink() { | ||
digestEdges = digestEdges.filter(link => link[0] !== this && link[1] !== this); | ||
digestSchedule = restoreNodeJobs(digestEdges, digestSchedule); | ||
/** | ||
* Unregisters connections between observables | ||
* @protected | ||
*/ | ||
_unlink() { | ||
Digest.unlink(this); | ||
}, | ||
consume(data, chew = identity) { | ||
dispatchDataConsume(() => { | ||
digestSchedule = scheduleJob(this, () => this.digest(chew(data)), digestSchedule); | ||
dispatchDataWrite(doNextJob); | ||
}); | ||
/** | ||
* Defers a digestion with a function | ||
* that can be relaced | ||
* without any data loss | ||
* @protected | ||
* @param {*|null|undefined} data | ||
* @optional | ||
* @param {function()|null} chew | ||
*/ | ||
_consume(data, chew = identity) { | ||
Digest.queue(this, () => this._digest(chew(data))); | ||
}, | ||
digest(data) { | ||
/** | ||
* Replace current state and push data further | ||
* @param {*|null|undefined} data | ||
*/ | ||
_digest(data) { | ||
if (shouldStructureBeReplaced(this.structure, data)) { | ||
this.structure = data; | ||
this.flush(); | ||
this._flush(data); | ||
} | ||
@@ -95,2 +70,12 @@ }, | ||
/** | ||
* Pushes data to all listeners | ||
* @param data | ||
* @private | ||
*/ | ||
_flush(data) { | ||
this._reactors.forEach(reaction => reaction(data)); | ||
}, | ||
/** | ||
* Registers a function that is going to be fed with new data pushing by this observable | ||
* @param {function} reaction | ||
@@ -100,5 +85,9 @@ * @returns {function()} | ||
appendReactor(reaction) { | ||
this.reactors = this.reactors.add(reaction); | ||
if (this._reactors.indexOf(reaction) < 0) { | ||
this._reactors.push(reaction); | ||
} | ||
return () => { | ||
this.reactors = this.reactors.delete(reaction); | ||
this._reactors = this._reactors.filter( | ||
registeredReaction => registeredReaction !== reaction | ||
); | ||
}; | ||
@@ -108,2 +97,4 @@ }, | ||
/** | ||
* Registers a function that is going to be fed with new data pushing by this observable. | ||
* Will launch provided function immediately. | ||
* @param {function} reaction | ||
@@ -117,10 +108,6 @@ * @returns {function()} | ||
flush() { | ||
this.reactors.forEach(reaction => reaction(this.read())); | ||
}, | ||
destroy() { | ||
this.unlink(); | ||
this._unlink(); | ||
this.structure = null; | ||
this.reactors = Set(); | ||
this._reactors = []; | ||
}, | ||
@@ -142,2 +129,27 @@ | ||
}, | ||
scan(valuesToRemember, initialValue) { | ||
const Scan = require('./Scan.js').default; | ||
return new Scan(this, valuesToRemember, initialValue); | ||
}, | ||
}; | ||
function shouldStructureBeReplaced(structure, candidate) { | ||
return ( | ||
hasValue(candidate) && ( | ||
!hasValue(structure) || | ||
!is(candidate, structure) | ||
) | ||
); | ||
}; | ||
function hasValue(v) { | ||
return ( | ||
v !== undefined && | ||
v !== null | ||
); | ||
} | ||
function identity(v) { | ||
return v; | ||
} |
@@ -12,6 +12,6 @@ import Reactor from './Reactor.js'; | ||
this.linkTo(source); | ||
this._linkTo(source); | ||
this.timeoutID = null; | ||
this.timeoutedData = null; | ||
this.digest(source.read()); | ||
this._digest(source.read()); | ||
this.subscription = source.appendReactor(data => { | ||
@@ -22,3 +22,3 @@ this.timeoutedData = data; | ||
this.timeoutID = null; | ||
this.consume(this.timeoutedData); | ||
this._consume(this.timeoutedData); | ||
}, timeout); | ||
@@ -29,15 +29,13 @@ } | ||
Throttle.prototype = { | ||
...Reactor.prototype, | ||
Throttle.prototype = Object.create(Reactor.prototype); | ||
destroy() { | ||
if (this.timeoutID) { | ||
clearTimeout(this.timeoutID); | ||
this.timeoutID = null; | ||
} | ||
if (this.subscription) { | ||
this.subscription(); | ||
} | ||
Reactor.prototype.destroy.call(this); | ||
}, | ||
Throttle.prototype.destroy = function () { | ||
if (this.timeoutID) { | ||
clearTimeout(this.timeoutID); | ||
this.timeoutID = null; | ||
} | ||
if (this.subscription) { | ||
this.subscription(); | ||
} | ||
Reactor.prototype.destroy.call(this); | ||
}; |
102
src/View.js
@@ -1,7 +0,5 @@ | ||
import { | ||
Map, | ||
} from 'immutable'; | ||
import ViewMergeMap from './ViewMergeMap'; | ||
import Reactor from './Reactor.js'; | ||
const identity = data => data; | ||
const errorPrefix = 'Immview::View: '; | ||
@@ -13,64 +11,62 @@ export default function View(source, process = identity) { | ||
if (source.subscribe) { | ||
this.connectToSource(source, process); | ||
this.unsubs = connectToSource(this, source, process); | ||
} else { | ||
this.connectToMultipleSources(source, process); | ||
this.unsubs = connectToMultipleSources(this, source, process); | ||
} | ||
return; | ||
} | ||
throw new Error(`${errorPrefix}No sources to plug in`); | ||
} | ||
View.prototype = { | ||
...Reactor.prototype, | ||
View.prototype = Object.create(Reactor.prototype); | ||
/** | ||
* @private | ||
* @param {Reactor} source | ||
*/ | ||
connectToSource(source, process) { | ||
this.linkTo(source); | ||
this.digest(process(source.read())); | ||
this.unsubs = [ | ||
source.appendReactor(data => this.consume(data, process)), | ||
]; | ||
}, | ||
View.prototype.destroy = function destroyView() { | ||
Reactor.prototype.destroy.call(this); | ||
/** | ||
* @private | ||
* @param {Object.<Reactor>} sources | ||
*/ | ||
connectToMultipleSources(sources, process) { | ||
// initialize as a map{string:Iterable} | ||
let mergedStructure = Map(); | ||
if (this.unsubs) { | ||
this.unsubs.forEach(func => func()); | ||
} | ||
const sourcesNames = Object.keys(sources); | ||
this.unsubs = null; | ||
}; | ||
// prefill mergedStructure before launching subscriptions | ||
sourcesNames.forEach(sourceName => { | ||
const source = sources[sourceName]; | ||
this.linkTo(source); | ||
const sourceData = source.read(); | ||
mergedStructure = mergedStructure.set(sourceName, sourceData); | ||
}); | ||
function identity(data) { | ||
return data; | ||
} | ||
// subscribe to all data changes in parent views | ||
this.unsubs = sourcesNames.map( | ||
sourceName => sources[sourceName].appendReactor( | ||
data => { | ||
mergedStructure = mergedStructure.set(sourceName, data); | ||
this.consume(mergedStructure, process); | ||
} | ||
) | ||
); | ||
function connectToSource(aView, source, process) { | ||
aView._linkTo(source); | ||
aView._digest(process(source.read())); | ||
return [ | ||
source.appendReactor(data => aView._consume(data, process)), | ||
]; | ||
} | ||
this.digest(process(mergedStructure)); | ||
}, | ||
function connectToMultipleSources(aView, sources, process) { | ||
// initialize as a map{string:Iterable} | ||
let mergedStructure = new ViewMergeMap(); | ||
destroy() { | ||
Reactor.prototype.destroy.call(this); | ||
const sourcesNames = Object.keys(sources); | ||
if (this.unsubs) { | ||
this.unsubs.forEach(func => func()); | ||
} | ||
// prefill mergedStructure before launching subscriptions | ||
sourcesNames.forEach(sourceName => { | ||
const source = sources[sourceName]; | ||
aView._linkTo(source); | ||
const sourceData = source.read(); | ||
mergedStructure = mergedStructure.set(sourceName, sourceData); | ||
}); | ||
this.unsubs = null; | ||
}, | ||
}; | ||
// subscribe to all data changes in parent views | ||
const unsubs = sourcesNames.map( | ||
sourceName => sources[sourceName].appendReactor( | ||
data => { | ||
mergedStructure = mergedStructure.set(sourceName, data); | ||
aView._consume(mergedStructure.clone(), process); | ||
} | ||
) | ||
); | ||
aView._digest(process(mergedStructure.clone())); | ||
return unsubs; | ||
} |
@@ -67,2 +67,3 @@ var fs = require('fs'); | ||
.catch(function(err) { | ||
console.error('ERROR!', err); | ||
process.exitCode = 1; | ||
@@ -69,0 +70,0 @@ }) |
@@ -1,2 +0,2 @@ | ||
import { Data, View, Domain } from '../src'; | ||
import { Data, View, Domain, dispatch } from '../src'; | ||
@@ -29,3 +29,3 @@ describe('branching and merged streams', () => { | ||
let hits = 0; | ||
let output; | ||
let output = 0; | ||
@@ -42,2 +42,22 @@ end.subscribe(v => { | ||
}); | ||
it('is reacting once to two dispatched write jobs affecting single child node', () => { | ||
const parent1 = new Data(1); | ||
const parent2 = new Data(2); | ||
let hits = []; | ||
new View( | ||
{ parent1, parent2 }, | ||
data => hits.push([ | ||
data.get('parent1'), | ||
data.get('parent2'), | ||
]) | ||
); | ||
dispatch(() => { | ||
parent1.write(11); | ||
parent2.write(22); | ||
}); | ||
// 1 - initial, 2 - rerender, 3+ - unnecessary | ||
expect(hits.length).toBe(2); | ||
}); | ||
}); |
@@ -12,5 +12,5 @@ import { Data, View } from '../src'; | ||
} | ||
expect(new NonStandardView().read()).toBe('a'); | ||
}); | ||
}); |
import Data from '../src/Data'; | ||
import * as I from 'immutable'; | ||
import { | ||
Map, | ||
fromJS, | ||
} from 'immutable'; | ||
describe('Data', () => { | ||
let d; | ||
let testDataObject; | ||
beforeEach(() => { | ||
d = new Data(I.Map({ a: 1, b: I.Map({ c: 2 }) })); | ||
testDataObject = new Data(Map({ a: 1, b: Map({ c: 2 }) })); | ||
}); | ||
it('can be created', () => { | ||
expect(!!d.subscribe).toBe(true); | ||
expect(!!testDataObject.subscribe).toBe(true); | ||
}); | ||
describe('can be created with a boolean value', () => { | ||
const boolDataTest = (startWith) => { | ||
const dataForBool = new Data(startWith); | ||
expect(dataForBool.read()).toBe(startWith); | ||
dataForBool.write(v => !v); | ||
expect(dataForBool.read()).toBe(!startWith); | ||
}; | ||
it('T', boolDataTest.bind(null, true)); | ||
it('F', boolDataTest.bind(null, false)); | ||
}); | ||
describe('can be created with a number', () => { | ||
const numberDataTest = (startWith) => { | ||
const dataForNumber = new Data(startWith); | ||
expect(dataForNumber.read()).toBe(startWith); | ||
dataForNumber.write(startWith + 1); | ||
expect(dataForNumber.read()).toBe(startWith + 1); | ||
}; | ||
it('2', numberDataTest.bind(null, 2)); | ||
it('0', numberDataTest.bind(null, 0)); | ||
it('-1', numberDataTest.bind(null, -1)); | ||
}); | ||
it('can be created with a string', () => { | ||
const dataForString = new Data(' 2 '); | ||
expect(dataForString.read()).toBe(' 2 '); | ||
dataForString.write(' 3 '); | ||
expect(dataForString.read()).toEqual(' 3 '); | ||
}); | ||
it('can be created with an object', () => { | ||
const dataForObject = new Data({ test: 1 }); | ||
expect(dataForObject.read()).toEqual({ test: 1 }); | ||
dataForObject.write({ test: 2 }); | ||
expect(dataForObject.read()).toEqual({ test: 2 }); | ||
}); | ||
it('can be created with an array', () => { | ||
const dataForObject = new Data([2, 3, 4]); | ||
expect(dataForObject.read()).toEqual([2, 3, 4]); | ||
dataForObject.write([2, 3, 4, 5]); | ||
expect(dataForObject.read()).toEqual([2, 3, 4, 5]); | ||
}); | ||
it('can be read from', () => { | ||
expect(d.read().get('a')).toBe(1); | ||
expect(d.read().getIn(['b', 'c'])).toBe(2); | ||
expect(d.read().toJS()).toEqual({ a: 1, b: { c: 2 } }); | ||
expect(testDataObject.read().get('a')).toBe(1); | ||
expect(testDataObject.read().getIn(['b', 'c'])).toBe(2); | ||
expect(testDataObject.read().toJS()).toEqual({ a: 1, b: { c: 2 } }); | ||
}); | ||
it('can be written to with a new data', () => { | ||
d.write(d.read().setIn(['b', 'c'], 3)); | ||
d.write(d.read().set('d', 3)); | ||
expect(d.read().getIn(['b', 'c'])).toBe(3); | ||
expect(d.read().get('d')).toBe(3); | ||
testDataObject.write(testDataObject.read().setIn(['b', 'c'], 3)); | ||
testDataObject.write(testDataObject.read().set('d', 3)); | ||
expect(testDataObject.read().getIn(['b', 'c'])).toBe(3); | ||
expect(testDataObject.read().get('d')).toBe(3); | ||
}); | ||
it('can be written to with a function returning data', () => { | ||
d.write(v => v.setIn(['b', 'c'], 3)); | ||
d.write(v => v.set('d', 3)); | ||
expect(d.read().getIn(['b', 'c'])).toBe(3); | ||
expect(d.read().get('d')).toBe(3); | ||
testDataObject.write(v => v.setIn(['b', 'c'], 3)); | ||
testDataObject.write(v => v.set('d', 3)); | ||
expect(testDataObject.read().getIn(['b', 'c'])).toBe(3); | ||
expect(testDataObject.read().get('d')).toBe(3); | ||
}); | ||
@@ -39,3 +86,3 @@ | ||
d.subscribe(state => { | ||
testDataObject.subscribe(state => { | ||
expect(state.get('a')).toBe(1); | ||
@@ -49,3 +96,3 @@ expect(state.get('d')).toBe(forthVal); | ||
forthVal = 3; | ||
d.write(d.read().set('d', forthVal)); | ||
testDataObject.write(testDataObject.read().set('d', forthVal)); | ||
}); | ||
@@ -57,3 +104,3 @@ | ||
const getDataMap = () => I.fromJS({ a: 1, b: { c: 2 } }); | ||
const getDataMap = () => fromJS({ a: 1, b: { c: 2 } }); | ||
@@ -77,7 +124,7 @@ const d = new Data(getDataMap()); | ||
let reactions = 0; | ||
const unsub = d.subscribe(() => { | ||
const unsub = testDataObject.subscribe(() => { | ||
reactions++; | ||
}); | ||
d.write(d.read().set('d', 3)); // change -> reaction | ||
testDataObject.write(testDataObject.read().set('d', 3)); // change -> reaction | ||
expect(reactions).toBe(2); | ||
@@ -87,3 +134,3 @@ | ||
d.write(d.read().set('d', 5)); // change -> no reaction | ||
testDataObject.write(testDataObject.read().set('d', 5)); // change -> no reaction | ||
expect(reactions).toBe(2); | ||
@@ -94,16 +141,23 @@ }); | ||
let reactions = 0; | ||
d.map(dData => { | ||
testDataObject.map(dData => { | ||
reactions += dData.get('a'); | ||
}); | ||
d.write(d.read().set('d', 3)); // change -> reaction | ||
testDataObject.write(testDataObject.read().set('d', 3)); // change -> reaction | ||
expect(reactions).toBe(2); | ||
}); | ||
it('writes can still be queued up and all performed', () => { | ||
const a = new Data(0); | ||
const b = new Data(0); | ||
expect(a.read()).toBe(0); | ||
expect(b.read()).toBe(0); | ||
a.write(i => { | ||
b.write(i => i + 1); | ||
// they are inside so they are actually queued | ||
b.write(i => i + 1); | ||
b.write(i => i + 1); | ||
// as they are queued, updates are not yet performed | ||
expect(a.read()).toBe(0); | ||
expect(b.read()).toBe(0); | ||
return i + 1; | ||
@@ -110,0 +164,0 @@ }); |
import Domain from '../src/Domain'; | ||
import Data from '../src/Data'; | ||
import View from '../src/View'; | ||
import { | ||
fromJS, | ||
} from 'immutable'; | ||
@@ -17,3 +20,3 @@ describe('Domain', () => { | ||
it('can be created from Data type', () => { | ||
const d = new Data({ a: 1 }); | ||
const d = new Data(fromJS({ a: 1 })); | ||
@@ -29,3 +32,3 @@ // this should be really tested here | ||
it('can be created from a View type', () => { | ||
const d = new Data({ a: 1 }); | ||
const d = new Data(fromJS({ a: 1 })); | ||
@@ -39,5 +42,5 @@ const v = new View(d, data => data); | ||
it('acquire queueable methods', () => { | ||
it('acquire queueable actions', () => { | ||
let testVar = ''; | ||
const d = new Data({ a: 1 }); | ||
const d = new Data(fromJS({ a: 1 })); | ||
const dmn = new Domain(d, { | ||
@@ -61,5 +64,18 @@ testMethod1: () => { | ||
it('enables to tell which action has what length', () => { | ||
const d = new Data(0); | ||
const dmn = new Domain(d, { doNothing: v => v }); | ||
// now, that doNothing on dmn is a new function | ||
// it doesn't inform about real action functions argument amount | ||
// and this information cannot be overridden | ||
expect(dmn.doNothing.length).toBe(0); | ||
// that's why it is being stored on separate property | ||
expect(dmn.doNothing.originalLength).toBe(1); | ||
}); | ||
it('allow subscriptions', () => { | ||
let testVar = 0; | ||
const d = new Data({ a: 1 }); | ||
const d = new Data(fromJS({ a: 1 })); | ||
const dmn = new Domain(d); | ||
@@ -82,3 +98,3 @@ | ||
let testVar = ''; | ||
const d = new Data({ a: 1 }); | ||
const d = new Data(fromJS({ a: 1 })); | ||
const dmn = new Domain(d, { | ||
@@ -85,0 +101,0 @@ testMethod1: () => { |
import { | ||
addEdge, | ||
getOrder, | ||
getGraphNodes, | ||
getAllNodes, | ||
getNodeChildren, | ||
visit, | ||
} from '../src/graphSort.js'; | ||
getOrderStartingFromNode, | ||
} from '../src/Graph.js'; | ||
describe('graphsort', () => { | ||
it('can get nodes', () => { | ||
expect(getGraphNodes([ | ||
expect(getAllNodes([ | ||
['a', 'c'], | ||
])).toEqual(['a', 'c']); | ||
expect(getGraphNodes([ | ||
expect(getAllNodes([ | ||
['a', 'b'], | ||
['a', 'c'], | ||
])).toEqual(['a', 'b', 'c']); | ||
expect(getGraphNodes([ | ||
expect(getAllNodes([ | ||
['a', 'b'], | ||
@@ -44,3 +46,3 @@ ['a', 'c'], | ||
]; | ||
const result = visit(edges, 'a', [], []); | ||
const result = getOrderStartingFromNode(edges, 'a', { visited: [], stack: [] }); | ||
expect(result.stack).toEqual(['b', 'f', 'c', 'd', 'a']); | ||
@@ -47,0 +49,0 @@ }); |
@@ -13,1 +13,2 @@ import './data.spec'; | ||
import './classExtending'; | ||
import './scan.spec'; |
@@ -15,6 +15,9 @@ import { | ||
expect(typeof instance.map).toBe('function'); | ||
expect(typeof instance.debounce).toBe('function'); | ||
expect(typeof instance.throttle).toBe('function'); | ||
expect(typeof instance.scan).toBe('function'); | ||
} | ||
describe('Immview', () => { | ||
describe('has public interface', () => { | ||
describe('keeps public interface in', () => { | ||
it('Data', () => { | ||
@@ -28,8 +31,12 @@ expect(Data).toBeDefined(); | ||
expect(View).toBeDefined(); | ||
const v = new View(); | ||
const d = new Data(); | ||
const v = new View(d); | ||
const v2 = new View({ d }); | ||
fulfillsReactorInterface(v); | ||
fulfillsReactorInterface(v2); | ||
}); | ||
it('Debounce', () => { | ||
expect(Debounce).toBeDefined(); | ||
const v = new View(); | ||
const d = new Data(); | ||
const v = new View(d); | ||
const instance = new Debounce(v); | ||
@@ -36,0 +43,0 @@ fulfillsReactorInterface(instance); |
@@ -8,3 +8,3 @@ import { | ||
copyQueueOntoSchedule, | ||
} from '../src/StreamSchedule'; | ||
} from '../src/Schedule'; | ||
@@ -11,0 +11,0 @@ const graph = [ |
import Domain from '../src/Domain'; | ||
import Data from '../src/Data'; | ||
import View from '../src/View'; | ||
import I from 'immutable'; | ||
import { | ||
Map, | ||
fromJS, | ||
} from 'immutable'; | ||
@@ -9,3 +12,3 @@ describe('View', function () { | ||
it('can be created from a Data', function () { | ||
var aData = new Data({ a: 1 }); | ||
var aData = new Data(Map({ a: 1 })); | ||
var resultView = new View(aData, i=>i); | ||
@@ -16,3 +19,3 @@ expect(resultView.read().get('a')).toBe(1); | ||
it('can be created from a View', function () { | ||
var aData = new Data({ a: 1 }); | ||
var aData = new Data(Map({ a: 1 })); | ||
var aView = new View(aData, i=>i); | ||
@@ -24,3 +27,3 @@ var resultView = new View(aView, i=>i); | ||
it('can be created from a Domain', function () { | ||
var aData = new Data({ a: 1 }); | ||
var aData = new Data(Map({ a: 1 })); | ||
var aView = new View(aData, i=>i); | ||
@@ -33,3 +36,3 @@ var aDomain = new Domain(aView, {}); | ||
it('can be created from a Domains', function () { | ||
var aData = new Data({ a: 1 }); | ||
var aData = new Data(Map({ a: 1 })); | ||
var aView = new View(aData, i=>i); | ||
@@ -47,3 +50,3 @@ var aDomain = new Domain(aView, {}); | ||
beforeEach(() => { | ||
d = new Data({ a: 1, b: { c: 2 } }); | ||
d = new Data(fromJS({ a: 1, b: { c: 2 } })); | ||
vReactions = 0; | ||
@@ -88,4 +91,4 @@ v = new View(d, state => { | ||
it('w/o processor func', function () { | ||
var d1 = new Data({ a: 1 }); | ||
var d2 = new Data({ a: 2 }); | ||
var d1 = new Data(Map({ a: 1 })); | ||
var d2 = new Data(Map({ a: 2 })); | ||
var v2 = new View({ d1, d2 }); | ||
@@ -104,8 +107,8 @@ expect(v2.read().toJS()).toEqual({ | ||
it('with processor func', function () { | ||
var d1 = new Data({ a: 1 }); | ||
var d2 = new Data({ a: 2 }); | ||
var d1 = new Data(Map({ a: 1 })); | ||
var d2 = new Data(Map({ a: 2 })); | ||
var v2 = new View({ d1, d2 }, data => { | ||
expect(data.get('d1')).toBeDefined(); | ||
expect(data.get('d2')).toBeDefined(); | ||
return I.Map({ | ||
return Map({ | ||
a: data.get('d1'), | ||
@@ -130,3 +133,3 @@ b: data.get('d2'), | ||
it('creates new View deriving from current with \'map\' functions', done => { | ||
new Domain(new Data(I.Map({ a: 1 })), {}) | ||
new Domain(new Data(Map({ a: 1 })), {}) | ||
.map(data => data.set('b', 2)) // <- view from data | ||
@@ -133,0 +136,0 @@ .map(data => data.setIn(['c'], 3)) // <- view from view |
# Simple usage example | ||
In order to create a complete **Domain** we have to create state stream and actions. | ||
This example requires you to know and understand [Immutable.js](https://facebook.github.io/immutable-js/) already. | ||
@@ -64,5 +65,14 @@ ## State | ||
```javascript | ||
ToDoDomain.subscribe(v => console.log(v.toJS())); | ||
// [] | ||
ToDoDomain.add('Eat a pizza'); | ||
// [{ label: "Eat a pizza" }] | ||
ToDoDomain.check(0); | ||
// [{ label: "Eat a pizza", done: true }] | ||
``` | ||
Please, be aware that all action calls and writes on **Data** instances are going to be dispatched to a execution queue that will run them in call order one after another with prioritization of **Data** writes. | ||
> Please, be aware that all action calls and writes on **Data** instances are going to be dispatched to a execution queue that will run them in call order one after another with prioritization of **Data** writes. This won't be even noticable in this example as calls in it are not nested in a way that would reveal it. | ||
Domain is now ready to be used to connect it a react component using [immview-react-connect](https://github.com/arturkulig/immview-react-connect). |
Sorry, the diff of this file is not supported yet
177791
41
2013
31