├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── src └── index.js ├── test └── index-spec.js └── webpack.config.babel.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": [ 4 | "transform-function-bind", 5 | "transform-es2015-modules-commonjs", 6 | "transform-object-rest-spread" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | node_modules 3 | lib 4 | temp 5 | dist 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jay Phelps 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # redux-observable-adapter-most 2 | 3 | Use [most.js](https://github.com/cujojs/most) with [redux-observable](https://github.com/redux-observable/redux-observable) 4 | 5 | 6 | ## Install 7 | 8 | This requires peer dependencies of `rxjs@5` and `most@1`, which will have to be installed as well. 9 | 10 | ```sh 11 | npm install --save redux-observable-adapter-most 12 | ``` 13 | 14 | ## Usage 15 | 16 | This library basically will convert the RxJS `ActionsObservable` provided to your Epics into a most.js version. Then the most.js Stream you return in your Epic will be converted back to an RxJS Observable inside the middleware. 17 | 18 | ```js 19 | import mostAdapter from 'redux-observable-adapter-most'; 20 | 21 | const epicMiddleware = createEpicMiddleware(rootEpic, { adapter: mostAdapter }); 22 | 23 | // your Epics are now most.js streams 24 | 25 | const pingPongEpic = action$ => 26 | action$.ofType(PING) 27 | .map(action => ({ 28 | type: PONG 29 | })); 30 | ``` 31 | 32 | The most.js Stream is an ActionsStream which has the equivalent `ofType` helper. If you prefer the functional composition way, you can use it too: 33 | 34 | ```js 35 | import { ofType } from 'redux-observable-adapter-most'; 36 | 37 | const pingPongEpic = action$ => { 38 | const ping$ = ofType(PING, action$); 39 | 40 | return map(action => ({ 41 | type: PONG 42 | }), ping$); 43 | }; 44 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-observable-adapter-most", 3 | "version": "0.0.2", 4 | "description": "Use most.js streams with redux-observable", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "build": "npm run build:cjs && npm run build:umd && npm run build:umd:min", 8 | "build:cjs": "babel src -d lib", 9 | "build:umd": "cross-env NODE_ENV=development webpack src/index.js dist/redux-observable-adapter-most.js", 10 | "build:umd:min": "cross-env NODE_ENV=production webpack src/index.js dist/redux-observable-adapter-most.min.js", 11 | "build:tests": "rm -rf temp && babel test -d temp", 12 | "clean": "rimraf temp lib dist", 13 | "test": "npm run build && npm run build:tests && mocha temp" 14 | }, 15 | "files": [ 16 | "dist", 17 | "lib", 18 | "README.md", 19 | "LICENSE" 20 | ], 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/redux-observable/redux-observable-adapter-most.git" 24 | }, 25 | "keywords": [ 26 | "RxJS", 27 | "most", 28 | "most.js", 29 | "compatibility", 30 | "interop", 31 | "convert", 32 | "support" 33 | ], 34 | "author": "Jay Phelps ", 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/redux-observable/redux-observable-adapter-most/issues" 38 | }, 39 | "homepage": "https://github.com/redux-observable/redux-observable-adapter-most", 40 | "peerDependencies": { 41 | "most": "^1.0.3", 42 | "rxjs": "^5.0.0-beta.6" 43 | }, 44 | "devDependencies": { 45 | "babel-cli": "^6.7.5", 46 | "babel-loader": "^6.2.4", 47 | "babel-plugin-transform-es2015-modules-commonjs": "^6.7.4", 48 | "babel-plugin-transform-function-bind": "^6.5.2", 49 | "babel-plugin-transform-object-rest-spread": "^6.6.5", 50 | "babel-polyfill": "^6.7.4", 51 | "babel-preset-es2015": "^6.6.0", 52 | "babel-register": "^6.9.0", 53 | "chai": "^3.5.0", 54 | "cross-env": "^1.0.8", 55 | "mocha": "^2.4.5", 56 | "rimraf": "^2.5.2", 57 | "most": "^1.0.3", 58 | "rxjs": "^5.0.0-beta.6", 59 | "sinon": "1.17.4", 60 | "webpack": "^1.13.1" 61 | } 62 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { Stream, filter } from 'most'; 2 | import { ObservableSource } from 'most/lib/observable/fromObservable'; 3 | import { from } from 'rxjs/observable/from'; 4 | 5 | export class ActionsStream extends Stream { 6 | ofType(...keys) { 7 | return ofType(...keys, this); 8 | } 9 | } 10 | 11 | export const ofType = (...keys) => { 12 | const source = keys.pop(); 13 | 14 | return filter(action => { 15 | const type = action.type; 16 | const len = keys.length; 17 | 18 | if (len === 1) { 19 | return type === keys[0]; 20 | } else { 21 | for (let i = 0; i < len; i++) { 22 | if (keys[i] === type) { 23 | return true; 24 | } 25 | } 26 | } 27 | return false; 28 | }, source); 29 | }; 30 | 31 | export default { 32 | input: input$ => new ActionsStream(new ObservableSource(input$)), 33 | output: output$ => from(output$) 34 | }; 35 | -------------------------------------------------------------------------------- /test/index-spec.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | import 'babel-polyfill'; 3 | import { expect } from 'chai'; 4 | import { spy } from 'sinon'; 5 | import { Observable } from 'rxjs/Observable'; 6 | import { of } from 'rxjs/observable/of'; 7 | import { toArray } from 'rxjs/operator/toArray'; 8 | import { Stream } from 'most'; 9 | import { ObservableSource } from 'most/lib/observable/fromObservable'; 10 | import adapter, { ActionsStream } from '../'; 11 | 12 | const streamToArray = stream => 13 | stream.reduce((a, x) => { 14 | a.push(x); 15 | return a; 16 | }, []); 17 | 18 | describe('adapter', () => { 19 | it('should convert input to most.js Stream', () => { 20 | const observable = Observable::of(1, 2, 3); 21 | const stream = adapter.input(observable); 22 | 23 | expect(stream).to.be.an.instanceof(Stream); 24 | 25 | return streamToArray(stream) 26 | .then(value => { 27 | expect(value).to.deep.equal([1, 2, 3]); 28 | }); 29 | }); 30 | 31 | it('should convert output to RxJS v5 Observable', (done) => { 32 | const stream = Stream.from([1, 2, 3]); 33 | const observable = adapter.output(stream); 34 | 35 | expect(observable).to.be.an.instanceof(Observable); 36 | 37 | observable::toArray().subscribe(value => { 38 | expect(value).to.deep.equal([1, 2, 3]); 39 | done(); 40 | }); 41 | }); 42 | }); 43 | 44 | describe('ActionsStream', () => { 45 | it('should support ofType operator', () => { 46 | const input$ = new ObservableSource( 47 | Observable::of({ type: 'A' }, { type: 'B' }, { type: 'A' }) 48 | ); 49 | const action$ = new ActionsStream(input$); 50 | 51 | expect(action$).to.be.an.instanceof(ActionsStream); 52 | 53 | return streamToArray(action$.ofType('A')) 54 | .then(value => { 55 | expect(value).to.deep.equal([ 56 | { type: 'A' }, 57 | { type: 'A' } 58 | ]); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | 3 | const env = process.env.NODE_ENV; 4 | 5 | const config = { 6 | module: { 7 | loaders: [ 8 | { test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ } 9 | ] 10 | }, 11 | output: { 12 | library: 'ReduxObservableAdapterMost', 13 | libraryTarget: 'umd' 14 | }, 15 | externals: { 16 | 'most': { 17 | root: 'most', 18 | commonjs2: 'most', 19 | commonjs: 'most', 20 | amd: 'most' 21 | } 22 | }, 23 | plugins: [ 24 | new webpack.optimize.OccurrenceOrderPlugin(), 25 | new webpack.DefinePlugin({ 26 | 'process.env.NODE_ENV': JSON.stringify(env) 27 | }) 28 | ] 29 | }; 30 | 31 | if (env === 'production') { 32 | config.plugins.push( 33 | new webpack.optimize.UglifyJsPlugin({ 34 | compressor: { 35 | pure_getters: true, 36 | unsafe: true, 37 | unsafe_comps: true, 38 | warnings: false 39 | } 40 | }) 41 | ); 42 | } 43 | 44 | export default config; 45 | --------------------------------------------------------------------------------