rename routeresolver methods to {onmatch,view}
expose some piecemeal modules rename internal xhr to request mirror internal stream api to match public api
This commit is contained in:
parent
8bb813155c
commit
80c25e3809
18 changed files with 276 additions and 264 deletions
|
|
@ -18,7 +18,6 @@ module.exports = function(renderer, pubsub) {
|
|||
run()
|
||||
|
||||
if (component === null) {
|
||||
pubsub.unsubscribe(root.redraw)
|
||||
delete root.redraw
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,16 @@ module.exports = function($window, renderer, pubsub) {
|
|||
var route = function(root, defaultRoute, routes) {
|
||||
var current = {path: null, component: "div"}
|
||||
var replay = router.defineRoutes(routes, function(payload, args, path, route) {
|
||||
if (typeof payload.view !== "function") {
|
||||
if (typeof payload.render !== "function") payload.render = function(vnode) {return vnode}
|
||||
var use = function(component) {
|
||||
args.path = path, args.route = route
|
||||
if (typeof payload.onmatch === "function") {
|
||||
if (typeof payload.view !== "function") payload.view = function(vnode) {return vnode}
|
||||
var resolve = function(component) {
|
||||
current.path = path, current.component = component
|
||||
renderer.render(root, payload.render(Vnode(component, null, args, undefined, undefined, undefined)))
|
||||
renderer.render(root, payload.view(Vnode(component, null, args, undefined, undefined, undefined)))
|
||||
}
|
||||
if (typeof payload.resolve !== "function") payload.resolve = function() {use(current.component)}
|
||||
if (path !== current.path) payload.resolve(use, args, path, route)
|
||||
else use(current.component)
|
||||
if (typeof payload.onmatch !== "function") payload.onmatch = function() {resolve(current.component)}
|
||||
if (path !== current.path) payload.onmatch(Vnode(payload, null, args, undefined, undefined, undefined), resolve)
|
||||
else resolve(current.component)
|
||||
}
|
||||
else {
|
||||
renderer.render(root, Vnode(payload, null, args, undefined, undefined, undefined))
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ o.spec("route", function() {
|
|||
})
|
||||
|
||||
function init(vnode) {
|
||||
o(vnode.attrs).deepEquals({})
|
||||
o(vnode.attrs.foo).equals(undefined)
|
||||
|
||||
done()
|
||||
}
|
||||
|
|
@ -231,8 +231,8 @@ o.spec("route", function() {
|
|||
})
|
||||
})
|
||||
|
||||
o("accepts object as payload", function(done) {
|
||||
var resolveCount = 0
|
||||
o("accepts RouteResolver", function(done) {
|
||||
var matchCount = 0
|
||||
var renderCount = 0
|
||||
var Component = {
|
||||
view: function() {
|
||||
|
|
@ -243,19 +243,19 @@ o.spec("route", function() {
|
|||
$window.location.href = prefix + "/"
|
||||
route(root, "/abc", {
|
||||
"/:id" : {
|
||||
resolve: function(use, args, path, route) {
|
||||
resolveCount++
|
||||
onmatch: function(vnode, resolve) {
|
||||
matchCount++
|
||||
|
||||
o(args).deepEquals({id: "abc"})
|
||||
o(path).equals("/abc")
|
||||
o(route).equals("/:id")
|
||||
o(vnode.attrs.id).equals("abc")
|
||||
o(vnode.attrs.path).equals("/abc")
|
||||
o(vnode.attrs.route).equals("/:id")
|
||||
|
||||
use(Component)
|
||||
resolve(Component)
|
||||
},
|
||||
render: function(vnode) {
|
||||
view: function(vnode) {
|
||||
renderCount++
|
||||
|
||||
o(vnode.attrs).deepEquals({id: "abc"})
|
||||
o(vnode.attrs.id).equals("abc")
|
||||
|
||||
return vnode
|
||||
},
|
||||
|
|
@ -263,7 +263,7 @@ o.spec("route", function() {
|
|||
})
|
||||
|
||||
setTimeout(function() {
|
||||
o(resolveCount).equals(1)
|
||||
o(matchCount).equals(1)
|
||||
o(renderCount).equals(1)
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
|
||||
|
|
@ -271,8 +271,8 @@ o.spec("route", function() {
|
|||
}, FRAME_BUDGET)
|
||||
})
|
||||
|
||||
o("accepts object without `render` method as payload", function(done) {
|
||||
var resolveCount = 0
|
||||
o("accepts RouteResolver without `view` method as payload", function(done) {
|
||||
var matchCount = 0
|
||||
var Component = {
|
||||
view: function() {
|
||||
return m("div")
|
||||
|
|
@ -282,20 +282,20 @@ o.spec("route", function() {
|
|||
$window.location.href = prefix + "/"
|
||||
route(root, "/abc", {
|
||||
"/:id" : {
|
||||
resolve: function(use, args, path, route) {
|
||||
resolveCount++
|
||||
onmatch: function(vnode, resolve) {
|
||||
matchCount++
|
||||
|
||||
o(args).deepEquals({id: "abc"})
|
||||
o(path).equals("/abc")
|
||||
o(route).equals("/:id")
|
||||
o(vnode.attrs.id).equals("abc")
|
||||
o(vnode.attrs.path).equals("/abc")
|
||||
o(vnode.attrs.route).equals("/:id")
|
||||
|
||||
use(Component)
|
||||
resolve(Component)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
o(resolveCount).equals(1)
|
||||
o(matchCount).equals(1)
|
||||
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
|
||||
|
|
@ -303,7 +303,7 @@ o.spec("route", function() {
|
|||
}, FRAME_BUDGET)
|
||||
})
|
||||
|
||||
o("accepts object without `resolve` method as payload", function(done) {
|
||||
o("object without `onmatch` method acts as component", function(done) {
|
||||
var renderCount = 0
|
||||
var Component = {
|
||||
view: function() {
|
||||
|
|
@ -314,10 +314,10 @@ o.spec("route", function() {
|
|||
$window.location.href = prefix + "/"
|
||||
route(root, "/abc", {
|
||||
"/:id" : {
|
||||
render: function(vnode) {
|
||||
view: function(vnode) {
|
||||
renderCount++
|
||||
|
||||
o(vnode.attrs).deepEquals({id: "abc"})
|
||||
o(vnode.attrs.id).equals("abc")
|
||||
|
||||
return m(Component)
|
||||
},
|
||||
|
|
@ -331,8 +331,8 @@ o.spec("route", function() {
|
|||
}, FRAME_BUDGET)
|
||||
})
|
||||
|
||||
o("calls resolve and render correct number of times", function(done) {
|
||||
var resolveCount = 0
|
||||
o("calls onmatch and view correct number of times", function(done) {
|
||||
var matchCount = 0
|
||||
var renderCount = 0
|
||||
var Component = {
|
||||
view: function() {
|
||||
|
|
@ -343,11 +343,11 @@ o.spec("route", function() {
|
|||
$window.location.href = prefix + "/"
|
||||
route(root, "/", {
|
||||
"/" : {
|
||||
resolve: function(use) {
|
||||
resolveCount++
|
||||
use(Component)
|
||||
onmatch: function(vnode, resolve) {
|
||||
matchCount++
|
||||
resolve(Component)
|
||||
},
|
||||
render: function(vnode) {
|
||||
view: function(vnode) {
|
||||
renderCount++
|
||||
return vnode
|
||||
},
|
||||
|
|
@ -355,13 +355,13 @@ o.spec("route", function() {
|
|||
})
|
||||
|
||||
callAsync(function() {
|
||||
o(resolveCount).equals(1)
|
||||
o(matchCount).equals(1)
|
||||
o(renderCount).equals(1)
|
||||
|
||||
redraw.publish()
|
||||
|
||||
setTimeout(function() {
|
||||
o(resolveCount).equals(1)
|
||||
o(matchCount).equals(1)
|
||||
o(renderCount).equals(2)
|
||||
|
||||
done()
|
||||
|
|
|
|||
|
|
@ -239,7 +239,9 @@ m(Header, {
|
|||
|
||||
#### Avoid component factories
|
||||
|
||||
Component diffing relies on strict equality checking, so you should avoid recreating components. Instead, consume components idiomatically.
|
||||
If you create a component from within a `view` method (either directly inline or by calling a function that does so), each redraw will have a different clone of the component. When diffing component vnodes, if the component referenced by the new vnode is not strictly equal to the one referenced by the old component, the two are assumed to be different components even if they ultimately run equivalent code. This means components created dynamically via a factory will always be re-created from scratch.
|
||||
|
||||
For that reason you should avoid recreating components. Instead, consume components idiomatically.
|
||||
|
||||
```javascript
|
||||
// AVOID
|
||||
|
|
|
|||
138
docs/route.md
138
docs/route.md
|
|
@ -6,15 +6,18 @@
|
|||
- [route.get](#route-get)
|
||||
- [route.prefix](#route-prefix)
|
||||
- [route.link](#route-link)
|
||||
- [RouteResolver](#routeresolver)
|
||||
- [routeResolver.onmatch](#routeresolver-onmatch)
|
||||
- [routeResolver.view](#routeresolver-view)
|
||||
- [How it works](#how-it-works)
|
||||
- [Typical usage](#typical-usage)
|
||||
- [Navigating to different routes](#navigating-to-different-routes)
|
||||
- [Routing parameters](#routing-parameters)
|
||||
- [Changing router prefix](#changing-router-prefix)
|
||||
- [Advanced component resolution](#advanced-component-resolution)
|
||||
- [Wrapping a layout component](#wrapping-a-layout-component)
|
||||
- [Advanced component resolution](#advanced-component-resolution)
|
||||
- [Authentication](#authentication)
|
||||
- [Lazy loading](#lazy-loading)
|
||||
- [Code splitting](#code-splitting)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -31,38 +34,6 @@ Argument | Type | Required | D
|
|||
|
||||
[How to read signatures](signatures.md)
|
||||
|
||||
#### RouteResolver
|
||||
|
||||
A RouterResolver is an object that contains a `resolve` and a `render` methods. Both methods are optional, but at least one must be defined.
|
||||
|
||||
`routeResolver = {resolve, render}`
|
||||
|
||||
##### routeResolver.resolve
|
||||
|
||||
The `resolve` method is called when the router needs to find a component to render. It is called once when a router path changes, but not on subsequent redraws. It can be used to run logic before a component initializes (for example authentication logic)
|
||||
|
||||
This method also allows you to asynchronously define what component will be rendered, making it suitable for code splitting and asynchronous module loading.
|
||||
|
||||
`routeResolver.resolve(use, args, path, route)`
|
||||
|
||||
Argument | Type | Required | Description
|
||||
----------- | --------------------- | -------- | ---
|
||||
`use` | `Function(Component)` | Yes | Call this function with a component as the first argument to use it as the route's component
|
||||
`args` | `Object` | No | The [routing parameters](#routing-parameters)
|
||||
`path` | `String` | No | The current router path, including interpolated routing parameter values, but without the prefix
|
||||
`route` | `String` | No | The matched route
|
||||
**returns** | | | Returns `undefined`
|
||||
|
||||
##### routeResolver.render
|
||||
|
||||
The `render` method is called on every redraw for a matching route. It is meant for functional composition of components, to avoid the need for repetitive component definitions
|
||||
|
||||
`vnode = routeResolve.render(vnode)`
|
||||
|
||||
Argument | Type | Required | Description
|
||||
----------- | --------------------- | -------- | ---
|
||||
`vnode` | `Vnode` | Yes | A [vnode](vnodes.md) whose attributes object contains routing parameters. If the routeResolver does not have a `resolve` method, the vnode defaults to a `div`
|
||||
**returns** | `Vnode` | | Returns a vnode
|
||||
|
||||
#### Static members
|
||||
|
||||
|
|
@ -109,6 +80,43 @@ Argument | Type | Required | Description
|
|||
`vnode` | `Vnode` | Yes | This method is meant to be used in conjunction with an `<a>` [vnode](vnodes.md)'s [`oncreate` hook](lifecycle-methods.md)
|
||||
**returns** | Function(e) | | Returns an event handler that calls `m.route.set` with the link's `href` as the `path`
|
||||
|
||||
#### RouteResolver
|
||||
|
||||
A RouterResolver is an object that contains an `onmatch` method, and optionally a `view` method.
|
||||
|
||||
`routeResolver = {onmatch, view}`
|
||||
|
||||
##### routeResolver.onmatch
|
||||
|
||||
The `onmatch` hook is called when the router needs to find a component to render. It is called once when a router path changes, but not on subsequent redraws. It can be used to run logic before a component initializes (for example authentication logic)
|
||||
|
||||
This method also allows you to asynchronously define what component will be rendered, making it suitable for code splitting and asynchronous module loading.
|
||||
|
||||
`routeResolver.onmatch(vnode, resolve)`
|
||||
|
||||
Argument | Type | Description
|
||||
------------------- | --------------------- | ---
|
||||
`vnode` | `Vnode` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If the routeResolver does not have a `resolve` method, the vnode's `tag` field defaults to a `div`
|
||||
`vnode.attrs` | `Object` | The [routing parameters](#routing-parameters)
|
||||
`vnode.attrs.path` | `String` | The current router path, including interpolated routing parameter values, but without the prefix. Same value as `m.route.get()`
|
||||
`vnode.attrs.route` | `String` | The matched route
|
||||
`resolve` | `Function(Component)` | Call this function with a component as the first argument to use it as the route's component
|
||||
**returns** | | Returns `undefined`
|
||||
|
||||
##### routeResolver.view
|
||||
|
||||
The `view` method is called on every redraw for a matching route. It is similar to the `view` method in components and it exists to simplify [component composition](#wrapping-a-layout-component).
|
||||
|
||||
`vnode = routeResolve.view(vnode)`
|
||||
|
||||
Argument | Type | Required | Description
|
||||
------------------- | --------------- | ---
|
||||
`vnode` | `Object` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If the routeResolver does not have a `resolve` method, the vnode's `tag` field defaults to a `div`
|
||||
`vnode.attrs` | `Object` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If the routeResolver does not have a `resolve` method, the vnode defaults to a `div`
|
||||
`vnode.attrs.path` | `String` | The current router path, including interpolated routing parameter values, but without the prefix. Same value as `m.route.get()`
|
||||
`vnode.attrs.route` | `String` | The matched route
|
||||
**returns** | `Vnode` | Returns a vnode
|
||||
|
||||
---
|
||||
|
||||
#### How it works
|
||||
|
|
@ -255,30 +263,9 @@ m.route.prefix("/my-app")
|
|||
|
||||
---
|
||||
|
||||
### Advanced component resolution
|
||||
|
||||
Instead of mapping a component to a route, you can specify a RouteResolver object. A RouteResolver object contains a `resolve()` and a `render()` method. Both methods are optional, but at least one of them should be specified.
|
||||
|
||||
```javascript
|
||||
m.route(document.body, "/", {
|
||||
"/": {
|
||||
resolve: function(use) {
|
||||
use(Home)
|
||||
},
|
||||
render: function(vnode) {
|
||||
return vnode
|
||||
},
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The RouteResolver can be used to implement a variety of advanced component initialization use cases.
|
||||
|
||||
---
|
||||
|
||||
### Wrapping a layout component
|
||||
|
||||
The RouterResolver's `render` method can be used to wrap a layout around a component, or to pass parameters to a top level component
|
||||
You can use anonymous components to wrap a layout around a component, or to pass parameters to a top level component
|
||||
|
||||
```javascript
|
||||
var Layout = {
|
||||
|
|
@ -289,7 +276,7 @@ var Layout = {
|
|||
|
||||
m.route(document.body, "/", {
|
||||
"/": {
|
||||
render: function() {
|
||||
view: function() {
|
||||
return m(Layout, Home)
|
||||
},
|
||||
}
|
||||
|
|
@ -298,9 +285,30 @@ m.route(document.body, "/", {
|
|||
|
||||
---
|
||||
|
||||
### Advanced component resolution
|
||||
|
||||
Instead of mapping a component to a route, you can specify a RouteResolver object. A RouteResolver object contains a `onmatch()` method and a optionally a `view()` method.
|
||||
|
||||
```javascript
|
||||
m.route(document.body, "/", {
|
||||
"/": {
|
||||
onmatch: function(vnode, resolve) {
|
||||
use(Home)
|
||||
},
|
||||
view: function(vnode) {
|
||||
return vnode
|
||||
},
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
RouteResolvers are useful for implementing a variety of advanced routing use cases.
|
||||
|
||||
---
|
||||
|
||||
### Authentication
|
||||
|
||||
The RouterResolver's `resolve` method can be used to run logic before component initialization (including asynchronous logic). The example below shows how to implement a login wall that prevents users from seeing the `/secret` page unless they login.
|
||||
The RouterResolver's `onmatch` hook can be used to run logic before the top level component in a route is initializated. The example below shows how to implement a login wall that prevents users from seeing the `/secret` page unless they login.
|
||||
|
||||
```javascript
|
||||
var isLoggedIn = false
|
||||
|
|
@ -320,8 +328,8 @@ var Login = {
|
|||
|
||||
m.route(document.body, "/secret", {
|
||||
"/secret": {
|
||||
resolve: function(use) {
|
||||
if (isLoggedIn) use(Home)
|
||||
onmatch: function(vnode, resolve) {
|
||||
if (isLoggedIn) resolve(Home)
|
||||
else m.route.set("/login")
|
||||
},
|
||||
},
|
||||
|
|
@ -329,15 +337,15 @@ m.route(document.body, "/secret", {
|
|||
})
|
||||
```
|
||||
|
||||
When the application loads, `resolve` is called and since `isLoggedIn` is false, the application redirects to `/login`. Once the user pressed the login button, `isLoggedIn` would be set to true, and the application would redirect to `/secret`. The `resolve` method would run once again, and since `isLoggedIn` is true this time, the application would render the `Home` component.
|
||||
When the application loads, `onmatch` is called and since `isLoggedIn` is false, the application redirects to `/login`. Once the user pressed the login button, `isLoggedIn` would be set to true, and the application would redirect to `/secret`. The `onmatch` hook would run once again, and since `isLoggedIn` is true this time, the application would render the `Home` component.
|
||||
|
||||
For the sake of simplicity, in the example above, the user's logged in status is kept in a global variable, and that flag is merely toggled when the user clicks the login button. In a real life application, a user would obviously have to supply proper login credentials, and clicking the login button would trigger a request to a server to authenticate the user.
|
||||
|
||||
---
|
||||
|
||||
### Lazy loading
|
||||
### Code splitting
|
||||
|
||||
One important feature of the `resolve` method in RouteResolvers is that the `use` callback can be triggered asynchronously. This allows components to be downloaded on demand.
|
||||
In a large application, it may be desirable to download the code for each route on demand, rather than upfront. Dividing the codebase this way is known as code splitting or lazy loading. In Mithril, this can be accomplished by calling the `resolve` callback of the `onmatch` hook asynchronously:
|
||||
|
||||
At its simplest form, one could do the following:
|
||||
|
||||
|
|
@ -368,8 +376,8 @@ function load(file, done) {
|
|||
|
||||
m.route(document.body, "/", {
|
||||
"/": {
|
||||
resolve: function(use) {
|
||||
load("Home.js", use)
|
||||
onmatch: function(vnode, resolve) {
|
||||
load("Home.js", resolve)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
16
index.js
16
index.js
|
|
@ -1,22 +1,20 @@
|
|||
"use strict"
|
||||
|
||||
var log = console.error.bind(console)
|
||||
var defaultStream = require("./stream/index")
|
||||
var m = require("./render/hyperscript")
|
||||
var renderService = require("./render/render")(window)
|
||||
var requestService = require("./request/request")(window, log)
|
||||
var redrawService = require("./api/pubsub")()
|
||||
var renderService = require("./render")
|
||||
var requestService = require("./request")
|
||||
var redrawService = require("./redraw")
|
||||
|
||||
requestService.setCompletionCallback(redrawService.publish)
|
||||
|
||||
m.route = require("./api/router")(window, renderService, redrawService)
|
||||
m.mount = require("./api/mount")(renderService, redrawService)
|
||||
m.route = require("./route")
|
||||
m.mount = require("./mount")
|
||||
m.trust = require("./render/trust")
|
||||
m.withAttr = require("./util/withAttr")
|
||||
m.prop = defaultStream
|
||||
m.prop = require("./stream")
|
||||
m.render = renderService.render
|
||||
m.redraw = redrawService.publish
|
||||
m.request = requestService.xhr
|
||||
m.request = requestService.request
|
||||
m.jsonp = requestService.jsonp
|
||||
m.version = "bleeding-edge"
|
||||
|
||||
|
|
|
|||
4
mount.js
Normal file
4
mount.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
var renderService = require("./render")
|
||||
var redrawService = require("./redraw")
|
||||
|
||||
module.exports = require("./api/mount")(renderService, redrawService)
|
||||
1
redraw.js
Normal file
1
redraw.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
module.exports = require("./api/pubsub")()
|
||||
1
render.js
Normal file
1
render.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
module.exports = require("./render/render")(window)
|
||||
1
request.js
Normal file
1
request.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
module.exports = require("./request/request")(window, console.error.bind(console))
|
||||
|
|
@ -10,8 +10,8 @@ module.exports = function($window, log) {
|
|||
var oncompletion
|
||||
function setCompletionCallback(callback) {oncompletion = callback}
|
||||
|
||||
function xhr(args) {
|
||||
var stream = Stream.stream()
|
||||
function request(args) {
|
||||
var stream = Stream()
|
||||
if (args.initialValue !== undefined) stream(args.initialValue)
|
||||
|
||||
var useBody = typeof args.useBody === "boolean" ? args.useBody : args.method !== "GET" && args.method !== "TRACE"
|
||||
|
|
@ -63,7 +63,7 @@ module.exports = function($window, log) {
|
|||
}
|
||||
|
||||
function jsonp(args) {
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
if (args.initialValue !== undefined) stream(args.initialValue)
|
||||
|
||||
var callbackName = args.callbackName || "_mithril_" + Math.round(Math.random() * 1e16) + "_" + callbackCount++
|
||||
|
|
@ -130,5 +130,5 @@ module.exports = function($window, log) {
|
|||
return data
|
||||
}
|
||||
|
||||
return {xhr: xhr, jsonp: jsonp, setCompletionCallback: setCompletionCallback}
|
||||
return {request: request, jsonp: jsonp, setCompletionCallback: setCompletionCallback}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<script src="../../querystring/build.js"></script>
|
||||
<script src="../../util/stream.js"></script>
|
||||
<script src="../../request/request.js"></script>
|
||||
<script src="test-xhr.js"></script>
|
||||
<script src="test-request.js"></script>
|
||||
<script src="test-jsonp.js"></script>
|
||||
|
||||
<script>require("../../ospec/ospec").run()</script>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ o.spec("xhr", function() {
|
|||
o.beforeEach(function() {
|
||||
mock = xhrMock()
|
||||
spy = o.spy()
|
||||
xhr = new Request(mock, spy).xhr
|
||||
xhr = new Request(mock, spy).request
|
||||
})
|
||||
|
||||
o.spec("success", function() {
|
||||
4
route.js
Normal file
4
route.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
var renderService = require("./render")
|
||||
var redrawService = require("./redraw")
|
||||
|
||||
module.exports = require("./api/router")(window, renderService, redrawService)
|
||||
2
stream.js
Normal file
2
stream.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
var StreamFactory = require("./util/stream")
|
||||
module.exports = StreamFactory(console.log.bind(console))
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
"use strict"
|
||||
|
||||
var log = console.error.bind(console)
|
||||
var StreamFactory = require("../util/stream")
|
||||
var Stream = StreamFactory(log)
|
||||
|
||||
var defaultStream = Stream.stream
|
||||
defaultStream.combine = Stream.combine
|
||||
defaultStream.reject = Stream.reject
|
||||
defaultStream.merge = Stream.merge
|
||||
defaultStream.HALT = Stream.HALT
|
||||
|
||||
module.exports = defaultStream
|
||||
|
|
@ -193,6 +193,10 @@ module.exports = function(log) {
|
|||
return streams.map(function(s) {return s()})
|
||||
}, streams)
|
||||
}
|
||||
createStream.merge = merge
|
||||
createStream.combine = combine
|
||||
createStream.reject = reject
|
||||
createStream.HALT = HALT
|
||||
|
||||
return {stream: createStream, merge: merge, combine: combine, reject: reject, HALT: HALT}
|
||||
return createStream
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ o.spec("stream", function() {
|
|||
|
||||
o.spec("stream", function() {
|
||||
o("works as getter/setter", function() {
|
||||
var stream = Stream.stream(1)
|
||||
var stream = Stream(1)
|
||||
var initialValue = stream()
|
||||
stream(2)
|
||||
var newValue = stream()
|
||||
|
|
@ -22,25 +22,25 @@ o.spec("stream", function() {
|
|||
o(newValue).equals(2)
|
||||
})
|
||||
o("has undefined value by default", function() {
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
|
||||
o(stream()).equals(undefined)
|
||||
})
|
||||
o("can update to undefined", function() {
|
||||
var stream = Stream.stream(1)
|
||||
var stream = Stream(1)
|
||||
stream(undefined)
|
||||
|
||||
o(stream()).equals(undefined)
|
||||
})
|
||||
o("can be stream of streams", function() {
|
||||
var stream = Stream.stream(Stream.stream(1))
|
||||
var stream = Stream(Stream(1))
|
||||
|
||||
o(stream()()).equals(1)
|
||||
})
|
||||
})
|
||||
o.spec("combine", function() {
|
||||
o("transforms value", function() {
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var doubled = Stream.combine(function(s) {return s() * 2}, [stream])
|
||||
|
||||
stream(2)
|
||||
|
|
@ -48,14 +48,14 @@ o.spec("stream", function() {
|
|||
o(doubled()).equals(4)
|
||||
})
|
||||
o("transforms default value", function() {
|
||||
var stream = Stream.stream(2)
|
||||
var stream = Stream(2)
|
||||
var doubled = Stream.combine(function(s) {return s() * 2}, [stream])
|
||||
|
||||
o(doubled()).equals(4)
|
||||
})
|
||||
o("transforms multiple values", function() {
|
||||
var s1 = Stream.stream()
|
||||
var s2 = Stream.stream()
|
||||
var s1 = Stream()
|
||||
var s2 = Stream()
|
||||
var added = Stream.combine(function(s1, s2) {return s1() + s2()}, [s1, s2])
|
||||
|
||||
s1(2)
|
||||
|
|
@ -64,15 +64,15 @@ o.spec("stream", function() {
|
|||
o(added()).equals(5)
|
||||
})
|
||||
o("transforms multiple default values", function() {
|
||||
var s1 = Stream.stream(2)
|
||||
var s2 = Stream.stream(3)
|
||||
var s1 = Stream(2)
|
||||
var s2 = Stream(3)
|
||||
var added = Stream.combine(function(s1, s2) {return s1() + s2()}, [s1, s2])
|
||||
|
||||
o(added()).equals(5)
|
||||
})
|
||||
o("transforms mixed default and late-bound values", function() {
|
||||
var s1 = Stream.stream(2)
|
||||
var s2 = Stream.stream()
|
||||
var s1 = Stream(2)
|
||||
var s2 = Stream()
|
||||
var added = Stream.combine(function(s1, s2) {return s1() + s2()}, [s1, s2])
|
||||
|
||||
s2(3)
|
||||
|
|
@ -81,7 +81,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("combines atomically", function() {
|
||||
var count = 0
|
||||
var a = Stream.stream()
|
||||
var a = Stream()
|
||||
var b = Stream.combine(function(a) {return a() * 2}, [a])
|
||||
var c = Stream.combine(function(a) {return a() * a()}, [a])
|
||||
var d = Stream.combine(function(b, c) {
|
||||
|
|
@ -96,7 +96,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("combines default value atomically", function() {
|
||||
var count = 0
|
||||
var a = Stream.stream(3)
|
||||
var a = Stream(3)
|
||||
var b = Stream.combine(function(a) {return a() * 2}, [a])
|
||||
var c = Stream.combine(function(a) {return a() * a()}, [a])
|
||||
var d = Stream.combine(function(b, c) {
|
||||
|
|
@ -109,8 +109,8 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("combine lists only changed upstreams in last arg", function() {
|
||||
var streams = []
|
||||
var a = Stream.stream()
|
||||
var b = Stream.stream()
|
||||
var a = Stream()
|
||||
var b = Stream()
|
||||
var c = Stream.combine(function(a, b, changed) {
|
||||
streams = changed
|
||||
}, [a, b])
|
||||
|
|
@ -123,8 +123,8 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("combine lists only changed upstreams in last arg with default value", function() {
|
||||
var streams = []
|
||||
var a = Stream.stream(3)
|
||||
var b = Stream.stream(5)
|
||||
var a = Stream(3)
|
||||
var b = Stream(5)
|
||||
var c = Stream.combine(function(a, b, changed) {
|
||||
streams = changed
|
||||
}, [a, b])
|
||||
|
|
@ -135,7 +135,7 @@ o.spec("stream", function() {
|
|||
o(streams[0]).equals(a)
|
||||
})
|
||||
o("combine can return undefined", function() {
|
||||
var a = Stream.stream(1)
|
||||
var a = Stream(1)
|
||||
var b = Stream.combine(function(a) {
|
||||
return undefined
|
||||
}, [a])
|
||||
|
|
@ -143,24 +143,24 @@ o.spec("stream", function() {
|
|||
o(b()).equals(undefined)
|
||||
})
|
||||
o("combine can return stream", function() {
|
||||
var a = Stream.stream(1)
|
||||
var a = Stream(1)
|
||||
var b = Stream.combine(function(a) {
|
||||
return Stream.stream(2)
|
||||
return Stream(2)
|
||||
}, [a])
|
||||
|
||||
o(b()()).equals(2)
|
||||
})
|
||||
o("combine can return pending stream", function() {
|
||||
var a = Stream.stream(1)
|
||||
var a = Stream(1)
|
||||
var b = Stream.combine(function(a) {
|
||||
return Stream.stream()
|
||||
return Stream()
|
||||
}, [a])
|
||||
|
||||
o(b()()).equals(undefined)
|
||||
})
|
||||
o("combine can halt", function() {
|
||||
var count = 0
|
||||
var a = Stream.stream(1)
|
||||
var a = Stream(1)
|
||||
var b = Stream.combine(function(a) {
|
||||
return Stream.HALT
|
||||
}, [a])
|
||||
|
|
@ -175,19 +175,19 @@ o.spec("stream", function() {
|
|||
o.spec("merge", function() {
|
||||
o("transforms an array of streams to an array of values", function() {
|
||||
var all = Stream.merge([
|
||||
Stream.stream(10),
|
||||
Stream.stream("20"),
|
||||
Stream.stream({value: 30}),
|
||||
Stream(10),
|
||||
Stream("20"),
|
||||
Stream({value: 30}),
|
||||
])
|
||||
|
||||
o(all()).deepEquals([10, "20", {value: 30}])
|
||||
})
|
||||
o("remains pending until all streams are active", function() {
|
||||
var straggler = Stream.stream()
|
||||
var straggler = Stream()
|
||||
|
||||
var all = Stream.merge([
|
||||
Stream.stream(10),
|
||||
Stream.stream("20"),
|
||||
Stream(10),
|
||||
Stream("20"),
|
||||
straggler,
|
||||
])
|
||||
|
||||
|
|
@ -199,7 +199,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o.spec("end", function() {
|
||||
o("end stream works", function() {
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var doubled = Stream.combine(function(stream) {return stream() * 2}, [stream])
|
||||
|
||||
stream.end(true)
|
||||
|
|
@ -209,7 +209,7 @@ o.spec("stream", function() {
|
|||
o(doubled()).equals(undefined)
|
||||
})
|
||||
o("end stream works with default value", function() {
|
||||
var stream = Stream.stream(2)
|
||||
var stream = Stream(2)
|
||||
var doubled = Stream.combine(function(stream) {return stream() * 2}, [stream])
|
||||
|
||||
stream.end(true)
|
||||
|
|
@ -219,7 +219,7 @@ o.spec("stream", function() {
|
|||
o(doubled()).equals(4)
|
||||
})
|
||||
o("cannot add downstream to ended stream", function() {
|
||||
var stream = Stream.stream(2)
|
||||
var stream = Stream(2)
|
||||
stream.end(true)
|
||||
|
||||
var doubled = Stream.combine(function(stream) {return stream() * 2}, [stream])
|
||||
|
|
@ -228,7 +228,7 @@ o.spec("stream", function() {
|
|||
o(doubled()).equals(undefined)
|
||||
})
|
||||
o("upstream does not affect ended stream", function() {
|
||||
var stream = Stream.stream(2)
|
||||
var stream = Stream(2)
|
||||
var doubled = Stream.combine(function(stream) {return stream() * 2}, [stream])
|
||||
|
||||
doubled.end(true)
|
||||
|
|
@ -240,7 +240,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o.spec("error", function() {
|
||||
o("error() works", function() {
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var errored = Stream.combine(function(stream) {throw new Error("error")}, [stream])
|
||||
|
||||
stream(3)
|
||||
|
|
@ -249,14 +249,14 @@ o.spec("stream", function() {
|
|||
o(errored.error().message).equals("error")
|
||||
})
|
||||
o("error() works with default value", function() {
|
||||
var stream = Stream.stream(3)
|
||||
var stream = Stream(3)
|
||||
var errored = Stream.combine(function(stream) {throw new Error("error")}, [stream])
|
||||
|
||||
o(errored()).equals(undefined)
|
||||
o(errored.error().message).equals("error")
|
||||
})
|
||||
o("error() removes error on valid value", function() {
|
||||
var stream = Stream.stream("a")
|
||||
var stream = Stream("a")
|
||||
var doubled = Stream.combine(function(stream) {
|
||||
if (typeof stream() !== "number") throw new Error("error")
|
||||
else return stream() * 2
|
||||
|
|
@ -269,7 +269,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("error() triggers catch", function() {
|
||||
var count = 0
|
||||
var stream = Stream.stream(1)
|
||||
var stream = Stream(1)
|
||||
var handled = stream.catch(function() {
|
||||
count++
|
||||
return 2
|
||||
|
|
@ -283,7 +283,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("thrown error propagates downstream", function() {
|
||||
var count = 0
|
||||
var stream = Stream.stream(1)
|
||||
var stream = Stream(1)
|
||||
.map(function() {throw new Error("error")})
|
||||
.map(function(value) {
|
||||
count++
|
||||
|
|
@ -300,7 +300,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("set error propagates downstream", function() {
|
||||
var count = 0
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var mapped = stream.map(function(value) {
|
||||
count++
|
||||
return value * 2
|
||||
|
|
@ -316,7 +316,7 @@ o.spec("stream", function() {
|
|||
o(count).equals(0)
|
||||
})
|
||||
o("error.map works", function() {
|
||||
var stream = Stream.stream(1)
|
||||
var stream = Stream(1)
|
||||
var mappedFromError = stream.error.map(function(value) {
|
||||
if (value) return "from" + value.message
|
||||
})
|
||||
|
|
@ -328,7 +328,7 @@ o.spec("stream", function() {
|
|||
o(mappedFromError()).equals("fromerror")
|
||||
})
|
||||
o("error from error.map propagates", function() {
|
||||
var stream = Stream.stream(1)
|
||||
var stream = Stream(1)
|
||||
var mappedFromError = stream.error.map(function(value) {
|
||||
return "from" + value.message
|
||||
})
|
||||
|
|
@ -344,7 +344,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("error thrown from error.map propagates downstream", function() {
|
||||
var count = 0
|
||||
var stream = Stream.stream(1)
|
||||
var stream = Stream(1)
|
||||
var mappedFromError = stream.error.map(function(value) {
|
||||
throw new Error("b")
|
||||
})
|
||||
|
|
@ -379,13 +379,13 @@ o.spec("stream", function() {
|
|||
o("error.map can return streams", function() {
|
||||
var stream = Stream.reject(new Error("error"))
|
||||
var error = stream.error.map(function(value) {
|
||||
return Stream.stream(1)
|
||||
return Stream(1)
|
||||
})
|
||||
|
||||
o(error()()).equals(1)
|
||||
})
|
||||
o("combined stream of two errored streams adopts error from first", function() {
|
||||
var a = Stream.stream(1)
|
||||
var a = Stream(1)
|
||||
var b = Stream.combine(function(a) {throw new Error("error from b")}, [a])
|
||||
var c = Stream.combine(function(a) {throw new Error("error from c")}, [a])
|
||||
var d = Stream.combine(function(b, c) {return 2}, [b, c])
|
||||
|
|
@ -443,7 +443,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o.spec("run", function() {
|
||||
o("works", function() {
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var doubled = stream.run(function(value) {return value * 2})
|
||||
|
||||
stream(3)
|
||||
|
|
@ -451,13 +451,13 @@ o.spec("stream", function() {
|
|||
o(doubled()).equals(6)
|
||||
})
|
||||
o("works with default value", function() {
|
||||
var stream = Stream.stream(3)
|
||||
var stream = Stream(3)
|
||||
var doubled = stream.run(function(value) {return value * 2})
|
||||
|
||||
o(doubled()).equals(6)
|
||||
})
|
||||
o("works with undefined value", function() {
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var mapped = stream.run(function(value) {return String(value)})
|
||||
|
||||
stream(undefined)
|
||||
|
|
@ -465,13 +465,13 @@ o.spec("stream", function() {
|
|||
o(mapped()).equals("undefined")
|
||||
})
|
||||
o("does not run when initialized w/ HALT", function() {
|
||||
var stream = Stream.stream(Stream.HALT)
|
||||
var stream = Stream(Stream.HALT)
|
||||
var mapped = stream.run(function(value) {return 123})
|
||||
|
||||
o(mapped()).equals(undefined)
|
||||
})
|
||||
o("does not run when set to HALT", function() {
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var mapped = stream.run(function(value) {return 123})
|
||||
|
||||
stream(Stream.HALT)
|
||||
|
|
@ -479,14 +479,14 @@ o.spec("stream", function() {
|
|||
o(mapped()).equals(undefined)
|
||||
})
|
||||
o("works with default undefined value", function() {
|
||||
var stream = Stream.stream(undefined)
|
||||
var stream = Stream(undefined)
|
||||
var mapped = stream.run(function(value) {return String(value)})
|
||||
|
||||
o(mapped()).equals("undefined")
|
||||
})
|
||||
o("works with stream that throws", function() {
|
||||
var count = 0
|
||||
var stream = Stream.stream(undefined)
|
||||
var stream = Stream(undefined)
|
||||
var errored = stream.run(function(value) {throw new Error("error")})
|
||||
var mapped = errored.map(function(value) {
|
||||
count++
|
||||
|
|
@ -501,8 +501,8 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("works with pending stream", function() {
|
||||
var count = 0
|
||||
var stream = Stream.stream(undefined)
|
||||
var absorbed = Stream.stream()
|
||||
var stream = Stream(undefined)
|
||||
var absorbed = Stream()
|
||||
var absorber = stream.run(function(value) {return absorbed})
|
||||
var mapped = absorber.map(function(value) {
|
||||
count++
|
||||
|
|
@ -518,14 +518,14 @@ o.spec("stream", function() {
|
|||
o(count).equals(1)
|
||||
})
|
||||
o("works with active stream", function() {
|
||||
var stream = Stream.stream(undefined)
|
||||
var mapped = stream.run(function(value) {return Stream.stream(1)})
|
||||
var stream = Stream(undefined)
|
||||
var mapped = stream.run(function(value) {return Stream(1)})
|
||||
|
||||
o(mapped()).equals(1)
|
||||
})
|
||||
o("works with errored stream", function() {
|
||||
var rejected
|
||||
var stream = Stream.stream(undefined)
|
||||
var stream = Stream(undefined)
|
||||
var mapped = stream.run(function(value) {
|
||||
return Stream.reject(new Error("error"))
|
||||
})
|
||||
|
|
@ -534,9 +534,9 @@ o.spec("stream", function() {
|
|||
o(mapped.error().message).equals("error")
|
||||
})
|
||||
o("works with ended stream", function() {
|
||||
var stream = Stream.stream(1)
|
||||
var stream = Stream(1)
|
||||
var mapped = stream.run(function(value) {
|
||||
var ended = Stream.stream(2)
|
||||
var ended = Stream(2)
|
||||
ended.end(true)
|
||||
return ended
|
||||
})
|
||||
|
|
@ -546,8 +546,8 @@ o.spec("stream", function() {
|
|||
o(mapped()).equals(2)
|
||||
})
|
||||
o("works when active stream updates", function() {
|
||||
var stream = Stream.stream(undefined)
|
||||
var absorbed = Stream.stream(1)
|
||||
var stream = Stream(undefined)
|
||||
var absorbed = Stream(1)
|
||||
var mapped = stream.run(function(value) {return absorbed})
|
||||
|
||||
absorbed(2)
|
||||
|
|
@ -560,8 +560,8 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("works when pending stream updates", function() {
|
||||
var count = 0
|
||||
var stream = Stream.stream(undefined)
|
||||
var absorbed = Stream.stream()
|
||||
var stream = Stream(undefined)
|
||||
var absorbed = Stream()
|
||||
var mapped = stream.run(function(value) {return absorbed})
|
||||
|
||||
mapped.map(function (value) {
|
||||
|
|
@ -577,8 +577,8 @@ o.spec("stream", function() {
|
|||
o(mapped()).equals(123)
|
||||
})
|
||||
o("works when updating stream to errored state", function() {
|
||||
var stream = Stream.stream(undefined)
|
||||
var absorbed = Stream.stream(1)
|
||||
var stream = Stream(undefined)
|
||||
var absorbed = Stream(1)
|
||||
var mapped = stream.run(function(value) {return absorbed})
|
||||
|
||||
absorbed.error(new Error("error"))
|
||||
|
|
@ -592,8 +592,8 @@ o.spec("stream", function() {
|
|||
o(mapped.error().message).equals("another error")
|
||||
})
|
||||
o("works when updating pending stream to errored state", function() {
|
||||
var stream = Stream.stream(undefined)
|
||||
var absorbed = Stream.stream()
|
||||
var stream = Stream(undefined)
|
||||
var absorbed = Stream()
|
||||
var mapped = stream.run(function(value) {return absorbed})
|
||||
|
||||
absorbed.error(new Error("error"))
|
||||
|
|
@ -602,8 +602,8 @@ o.spec("stream", function() {
|
|||
o(mapped.error().message).equals("error")
|
||||
})
|
||||
o("works when updating stream to active state", function() {
|
||||
var stream = Stream.stream(undefined)
|
||||
var absorbed = Stream.stream(1)
|
||||
var stream = Stream(undefined)
|
||||
var absorbed = Stream(1)
|
||||
var mapped = stream.run(function(value) {return absorbed})
|
||||
|
||||
absorbed.error(new Error("error"))
|
||||
|
|
@ -617,8 +617,8 @@ o.spec("stream", function() {
|
|||
o(mapped.error()).equals(undefined)
|
||||
})
|
||||
o("throwing from absorbed propagates", function() {
|
||||
var stream = Stream.stream(undefined)
|
||||
var absorbedParent = Stream.stream()
|
||||
var stream = Stream(undefined)
|
||||
var absorbedParent = Stream()
|
||||
var absorbed = absorbedParent.map(function() {throw new Error("error")})
|
||||
var mapped = stream.run(function(value) {return absorbed})
|
||||
|
||||
|
|
@ -648,7 +648,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("catch works from combine", function() {
|
||||
var count = 0
|
||||
var stream = Stream.combine(function() {throw new Error("error")}, [Stream.stream(1)]).catch(function(e) {
|
||||
var stream = Stream.combine(function() {throw new Error("error")}, [Stream(1)]).catch(function(e) {
|
||||
count++
|
||||
return "no" + e.message
|
||||
})
|
||||
|
|
@ -662,7 +662,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("catch is not called if no error", function() {
|
||||
var count = 0
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var handled = stream.map(function(value) {return value + value}).catch(function(e) {
|
||||
count++
|
||||
return "no" + e.message
|
||||
|
|
@ -679,7 +679,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("catch is not called if no error with default value", function() {
|
||||
var count = 0
|
||||
var stream = Stream.stream("a").map(function(value) {return value + value}).catch(function(e) {
|
||||
var stream = Stream("a").map(function(value) {return value + value}).catch(function(e) {
|
||||
count++
|
||||
return "no" + e.message
|
||||
})
|
||||
|
|
@ -710,7 +710,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("catch absorbs pending stream", function() {
|
||||
var count = 0
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var mapped = Stream.reject(new Error("b")).catch(function(e) {
|
||||
return stream
|
||||
})
|
||||
|
|
@ -723,7 +723,7 @@ o.spec("stream", function() {
|
|||
o(count).equals(0)
|
||||
})
|
||||
o("catch absorbs active stream", function() {
|
||||
var stream = Stream.stream(1)
|
||||
var stream = Stream(1)
|
||||
var mapped = Stream.reject(new Error("b")).catch(function(e) {
|
||||
return stream
|
||||
})
|
||||
|
|
@ -752,7 +752,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("catches wrapped rejected stream", function() {
|
||||
var caught
|
||||
var stream = Stream.stream(1).map(function() {
|
||||
var stream = Stream(1).map(function() {
|
||||
return Stream.reject(new Error("error"))
|
||||
})
|
||||
.catch(function(value) {
|
||||
|
|
@ -767,8 +767,8 @@ o.spec("stream", function() {
|
|||
})
|
||||
o("catches nested wrapped rejected stream", function() {
|
||||
var caught
|
||||
var stream = Stream.stream(1).map(function() {
|
||||
return Stream.stream(2).map(function() {
|
||||
var stream = Stream(1).map(function() {
|
||||
return Stream(2).map(function() {
|
||||
return Stream.reject(new Error("error"))
|
||||
})
|
||||
})
|
||||
|
|
@ -785,56 +785,56 @@ o.spec("stream", function() {
|
|||
})
|
||||
o.spec("valueOf", function() {
|
||||
o("works", function() {
|
||||
o(Stream.stream(1).valueOf()).equals(1)
|
||||
o(Stream.stream("a").valueOf()).equals("a")
|
||||
o(Stream.stream(true).valueOf()).equals(true)
|
||||
o(Stream.stream(null).valueOf()).equals(null)
|
||||
o(Stream.stream(undefined).valueOf()).equals(undefined)
|
||||
o(Stream.stream({a: 1}).valueOf()).deepEquals({a: 1})
|
||||
o(Stream.stream([1, 2, 3]).valueOf()).deepEquals([1, 2, 3])
|
||||
o(Stream.stream().valueOf()).equals(undefined)
|
||||
o(Stream(1).valueOf()).equals(1)
|
||||
o(Stream("a").valueOf()).equals("a")
|
||||
o(Stream(true).valueOf()).equals(true)
|
||||
o(Stream(null).valueOf()).equals(null)
|
||||
o(Stream(undefined).valueOf()).equals(undefined)
|
||||
o(Stream({a: 1}).valueOf()).deepEquals({a: 1})
|
||||
o(Stream([1, 2, 3]).valueOf()).deepEquals([1, 2, 3])
|
||||
o(Stream().valueOf()).equals(undefined)
|
||||
})
|
||||
o("allows implicit value access in mathematical operations", function() {
|
||||
o(Stream.stream(1) + Stream.stream(1)).equals(2)
|
||||
o(Stream(1) + Stream(1)).equals(2)
|
||||
})
|
||||
})
|
||||
o.spec("toString", function() {
|
||||
o("aliases valueOf", function() {
|
||||
var stream = Stream.stream(1)
|
||||
var stream = Stream(1)
|
||||
|
||||
o(stream.toString).equals(stream.valueOf)
|
||||
})
|
||||
o("allows implicit value access in string operations", function() {
|
||||
o(Stream.stream("a") + Stream.stream("b")).equals("ab")
|
||||
o(Stream("a") + Stream("b")).equals("ab")
|
||||
})
|
||||
})
|
||||
o.spec("toJSON", function() {
|
||||
o("works", function() {
|
||||
o(Stream.stream(1).toJSON()).equals(1)
|
||||
o(Stream.stream("a").toJSON()).equals("a")
|
||||
o(Stream.stream(true).toJSON()).equals(true)
|
||||
o(Stream.stream(null).toJSON()).equals(null)
|
||||
o(Stream.stream(undefined).toJSON()).equals(undefined)
|
||||
o(Stream.stream({a: 1}).toJSON()).deepEquals({a: 1})
|
||||
o(Stream.stream([1, 2, 3]).toJSON()).deepEquals([1, 2, 3])
|
||||
o(Stream.stream().toJSON()).equals(undefined)
|
||||
o(Stream.stream(new Date(0)).toJSON()).equals(new Date(0).toJSON())
|
||||
o(Stream(1).toJSON()).equals(1)
|
||||
o(Stream("a").toJSON()).equals("a")
|
||||
o(Stream(true).toJSON()).equals(true)
|
||||
o(Stream(null).toJSON()).equals(null)
|
||||
o(Stream(undefined).toJSON()).equals(undefined)
|
||||
o(Stream({a: 1}).toJSON()).deepEquals({a: 1})
|
||||
o(Stream([1, 2, 3]).toJSON()).deepEquals([1, 2, 3])
|
||||
o(Stream().toJSON()).equals(undefined)
|
||||
o(Stream(new Date(0)).toJSON()).equals(new Date(0).toJSON())
|
||||
})
|
||||
o("works w/ JSON.stringify", function() {
|
||||
o(JSON.stringify(Stream.stream(1))).equals(JSON.stringify(1))
|
||||
o(JSON.stringify(Stream.stream("a"))).equals(JSON.stringify("a"))
|
||||
o(JSON.stringify(Stream.stream(true))).equals(JSON.stringify(true))
|
||||
o(JSON.stringify(Stream.stream(null))).equals(JSON.stringify(null))
|
||||
o(JSON.stringify(Stream.stream(undefined))).equals(JSON.stringify(undefined))
|
||||
o(JSON.stringify(Stream.stream({a: 1}))).deepEquals(JSON.stringify({a: 1}))
|
||||
o(JSON.stringify(Stream.stream([1, 2, 3]))).deepEquals(JSON.stringify([1, 2, 3]))
|
||||
o(JSON.stringify(Stream.stream())).equals(JSON.stringify(undefined))
|
||||
o(JSON.stringify(Stream.stream(new Date(0)))).equals(JSON.stringify(new Date(0)))
|
||||
o(JSON.stringify(Stream(1))).equals(JSON.stringify(1))
|
||||
o(JSON.stringify(Stream("a"))).equals(JSON.stringify("a"))
|
||||
o(JSON.stringify(Stream(true))).equals(JSON.stringify(true))
|
||||
o(JSON.stringify(Stream(null))).equals(JSON.stringify(null))
|
||||
o(JSON.stringify(Stream(undefined))).equals(JSON.stringify(undefined))
|
||||
o(JSON.stringify(Stream({a: 1}))).deepEquals(JSON.stringify({a: 1}))
|
||||
o(JSON.stringify(Stream([1, 2, 3]))).deepEquals(JSON.stringify([1, 2, 3]))
|
||||
o(JSON.stringify(Stream())).equals(JSON.stringify(undefined))
|
||||
o(JSON.stringify(Stream(new Date(0)))).equals(JSON.stringify(new Date(0)))
|
||||
})
|
||||
})
|
||||
o.spec("uncaught exception reporting", function() {
|
||||
o("reports thrown errors", function(done) {
|
||||
Stream.stream(1).map(function() {throw new Error("error")})
|
||||
Stream(1).map(function() {throw new Error("error")})
|
||||
|
||||
setTimeout(function() {
|
||||
o(spy.callCount).equals(1)
|
||||
|
|
@ -853,7 +853,7 @@ o.spec("stream", function() {
|
|||
})
|
||||
o.spec("map", function() {
|
||||
o("works", function() {
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var doubled = stream.map(function(value) {return value * 2})
|
||||
|
||||
stream(3)
|
||||
|
|
@ -861,13 +861,13 @@ o.spec("stream", function() {
|
|||
o(doubled()).equals(6)
|
||||
})
|
||||
o("works with default value", function() {
|
||||
var stream = Stream.stream(3)
|
||||
var stream = Stream(3)
|
||||
var doubled = stream.map(function(value) {return value * 2})
|
||||
|
||||
o(doubled()).equals(6)
|
||||
})
|
||||
o("works with undefined value", function() {
|
||||
var stream = Stream.stream()
|
||||
var stream = Stream()
|
||||
var mapped = stream.map(function(value) {return String(value)})
|
||||
|
||||
stream(undefined)
|
||||
|
|
@ -875,22 +875,22 @@ o.spec("stream", function() {
|
|||
o(mapped()).equals("undefined")
|
||||
})
|
||||
o("works with default undefined value", function() {
|
||||
var stream = Stream.stream(undefined)
|
||||
var stream = Stream(undefined)
|
||||
var mapped = stream.map(function(value) {return String(value)})
|
||||
|
||||
o(mapped()).equals("undefined")
|
||||
})
|
||||
o("works with pending stream", function() {
|
||||
var stream = Stream.stream(undefined)
|
||||
var mapped = stream.map(function(value) {return Stream.stream()})
|
||||
var stream = Stream(undefined)
|
||||
var mapped = stream.map(function(value) {return Stream()})
|
||||
|
||||
o(mapped()()).equals(undefined)
|
||||
})
|
||||
})
|
||||
o.spec("ap", function() {
|
||||
o("works", function() {
|
||||
var apply = Stream.stream(function(value) {return value * 2})
|
||||
var stream = Stream.stream(3)
|
||||
var apply = Stream(function(value) {return value * 2})
|
||||
var stream = Stream(3)
|
||||
var applied = apply.ap(stream)
|
||||
|
||||
o(applied()).equals(6)
|
||||
|
|
@ -904,8 +904,8 @@ o.spec("stream", function() {
|
|||
o(applied()).equals(3)
|
||||
})
|
||||
o("works with undefined value", function() {
|
||||
var apply = Stream.stream(function(value) {return String(value)})
|
||||
var stream = Stream.stream(undefined)
|
||||
var apply = Stream(function(value) {return String(value)})
|
||||
var stream = Stream(undefined)
|
||||
var applied = apply.ap(stream)
|
||||
|
||||
o(applied()).equals("undefined")
|
||||
|
|
@ -918,7 +918,7 @@ o.spec("stream", function() {
|
|||
o.spec("fantasy-land", function() {
|
||||
o.spec("functor", function() {
|
||||
o("identity", function() {
|
||||
var stream = Stream.stream(3)
|
||||
var stream = Stream(3)
|
||||
var mapped = stream.map(function(value) {return value})
|
||||
|
||||
o(stream()).equals(mapped())
|
||||
|
|
@ -927,7 +927,7 @@ o.spec("stream", function() {
|
|||
function f(x) {return x * 2}
|
||||
function g(x) {return x * x}
|
||||
|
||||
var stream = Stream.stream(3)
|
||||
var stream = Stream(3)
|
||||
|
||||
var mapped = stream.map(function(value) {return f(g(value))})
|
||||
var composed = stream.map(g).map(f)
|
||||
|
|
@ -938,9 +938,9 @@ o.spec("stream", function() {
|
|||
})
|
||||
o.spec("apply", function() {
|
||||
o("composition", function() {
|
||||
var a = Stream.stream(function(value) {return value * 2})
|
||||
var u = Stream.stream(function(value) {return value * 3})
|
||||
var v = Stream.stream(5)
|
||||
var a = Stream(function(value) {return value * 2})
|
||||
var u = Stream(function(value) {return value * 3})
|
||||
var v = Stream(5)
|
||||
|
||||
var mapped = a.map(function(f) {
|
||||
return function(g) {
|
||||
|
|
@ -958,14 +958,14 @@ o.spec("stream", function() {
|
|||
})
|
||||
o.spec("applicative", function() {
|
||||
o("identity", function() {
|
||||
var a = Stream.stream().of(function(value) {return value})
|
||||
var v = Stream.stream(5)
|
||||
var a = Stream().of(function(value) {return value})
|
||||
var v = Stream(5)
|
||||
|
||||
o(a.ap(v)()).equals(5)
|
||||
o(a.ap(v)()).equals(v())
|
||||
})
|
||||
o("homomorphism", function() {
|
||||
var a = Stream.stream(0)
|
||||
var a = Stream(0)
|
||||
var f = function(value) {return value * 2}
|
||||
var x = 3
|
||||
|
||||
|
|
@ -973,8 +973,8 @@ o.spec("stream", function() {
|
|||
o(a.of(f).ap(a.of(x))()).equals(a.of(f(x))())
|
||||
})
|
||||
o("interchange", function() {
|
||||
var u = Stream.stream(function(value) {return value * 2})
|
||||
var a = Stream.stream()
|
||||
var u = Stream(function(value) {return value * 2})
|
||||
var a = Stream()
|
||||
var y = 3
|
||||
|
||||
o(u.ap(a.of(y))()).equals(6)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue