├── .gitignore ├── .gitattributes ├── .travis.yml ├── .editorconfig ├── package.json ├── index.js ├── license ├── readme.md └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '5' 4 | - '4' 5 | - '0.12' 6 | - '0.10' 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsify", 3 | "version": "1.0.0", 4 | "description": "Observableify a callback-style function", 5 | "license": "MIT", 6 | "repository": "SamVerschueren/obsify", 7 | "author": { 8 | "name": "Sam Verschueren", 9 | "email": "sam.verschueren@gmail.com", 10 | "url": "github.com/SamVerschueren" 11 | }, 12 | "engines": { 13 | "node": ">=0.10.0" 14 | }, 15 | "scripts": { 16 | "test": "xo && ava" 17 | }, 18 | "files": [ 19 | "index.js" 20 | ], 21 | "keywords": [ 22 | "observable", 23 | "observables", 24 | "observableify", 25 | "callback", 26 | "cb", 27 | "node", 28 | "subscribe", 29 | "convert", 30 | "transform", 31 | "wrap", 32 | "wrapper", 33 | "bind", 34 | "to" 35 | ], 36 | "dependencies": { 37 | "rxjs": "^5.0.0-beta.6" 38 | }, 39 | "devDependencies": { 40 | "ava": "*", 41 | "xo": "*" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Observable = require('rxjs').Observable; 3 | 4 | var processFn = function (fn) { 5 | return Observable.bindNodeCallback(fn); 6 | }; 7 | 8 | module.exports = function (obj, opts) { 9 | opts = opts || {}; 10 | var exclude = opts.exclude || [/.+Sync$/]; 11 | 12 | var filter = function (key) { 13 | var match = function (pattern) { 14 | return typeof pattern === 'string' ? key === pattern : pattern.test(key); 15 | }; 16 | 17 | return opts.include ? opts.include.some(match) : !exclude.some(match); 18 | }; 19 | 20 | var ret = typeof obj === 'function' ? function () { 21 | if (opts.excludeMain) { 22 | return obj.apply(this, arguments); 23 | } 24 | 25 | return processFn(obj).apply(this, arguments); 26 | } : {}; 27 | 28 | return Object.keys(obj).reduce(function (ret, key) { 29 | var x = obj[key]; 30 | 31 | ret[key] = typeof x === 'function' && filter(key) ? processFn(x) : x; 32 | 33 | return ret; 34 | }, ret); 35 | }; 36 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Sam Verschueren (github.com/SamVerschueren) 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # obsify [![Build Status](https://travis-ci.org/SamVerschueren/obsify.svg?branch=master)](https://travis-ci.org/SamVerschueren/obsify) 2 | 3 | > Observableify a callback-style function 4 | 5 | 6 | ## Install 7 | 8 | ``` 9 | $ npm install --save obsify 10 | ``` 11 | 12 | 13 | ## Usage 14 | 15 | ```js 16 | const fs = require('fs'); 17 | const obsify = require('obsify'); 18 | 19 | obsify(fs.readFile)('package.json', 'utf8') 20 | .map(data => JSON.parse(data)) 21 | .subscribe(data => { 22 | console.log(data.name); 23 | //=> 'obsify' 24 | }); 25 | 26 | // or observableify all methods in a module 27 | 28 | obsify(fs).readFile('package.json', 'utf8') 29 | .map(data => JSON.parse(data)) 30 | .subscribe(data => { 31 | console.log(data.name); 32 | //=> 'obsify' 33 | }); 34 | ``` 35 | 36 | 37 | ## API 38 | 39 | ### obsify(input, [options]) 40 | 41 | #### input 42 | 43 | Type: `function`, `object` 44 | 45 | Callback-style function or module whose methods you want to observableify. 46 | 47 | 48 | #### options 49 | 50 | ##### include 51 | 52 | Type: `array` of (`string`|`regex`) 53 | 54 | Methods in a module to observableify. Remaining methods will be left untouched. 55 | 56 | ##### exclude 57 | 58 | Type: `array` of (`string`|`regex`)
59 | Default: `[/.+Sync$/]` 60 | 61 | Methods in a module **not** to observableify. Methods with names ending with `'Sync'` are excluded by default. 62 | 63 | ##### excludeMain 64 | 65 | Type: `boolean`
66 | Default: `false` 67 | 68 | By default, if given module is a function itself, this function will be observableified. Turn this option on if you want to observableify only methods of the module. 69 | 70 | ```js 71 | const obsify = require('obsify'); 72 | 73 | function fn() { 74 | return true; 75 | } 76 | 77 | fn.method = (data, callback) => { 78 | setImmediate(() => { 79 | callback(data, null); 80 | }); 81 | }; 82 | 83 | // observableify methods but not fn() 84 | const observableFn = obsify(fn, {excludeMain: true}); 85 | 86 | if (observableFn()) { 87 | observableFn.method('hi').subscribe(data => { 88 | console.log(data); 89 | }); 90 | } 91 | ``` 92 | 93 | 94 | ## License 95 | 96 | MIT © [Sam Verschueren](https://github.com/SamVerschueren) 97 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import test from 'ava'; 3 | import m from './'; 4 | 5 | const fixture = cb => setImmediate(() => cb(null, 'unicorn')); 6 | const fixture2 = (x, cb) => setImmediate(() => cb(null, x)); 7 | const fixture3 = () => 'rainbow'; 8 | const fixture4 = cb => setImmediate(() => { 9 | cb(null, 'unicorn'); 10 | return 'rainbow'; 11 | }); 12 | 13 | fixture4.meow = cb => setImmediate(() => cb(null, 'unicorn')); 14 | 15 | const fixtureModule = { 16 | method1: fixture, 17 | method2: fixture, 18 | method3: fixture3 19 | }; 20 | 21 | test.cb('main', t => { 22 | t.is(typeof m(fixture)().subscribe, 'function'); 23 | m(fixture)().subscribe(result => { 24 | t.is(result, 'unicorn'); 25 | t.end(); 26 | }); 27 | }); 28 | 29 | test.cb('pass argument', t => { 30 | m(fixture2)('rainbow').subscribe(result => { 31 | t.is(result, 'rainbow'); 32 | t.end(); 33 | }); 34 | }); 35 | 36 | test.cb('wrap core method', t => { 37 | m(fs.readFile)('package.json', 'utf8') 38 | .map(content => JSON.parse(content)) 39 | .subscribe(result => { 40 | t.is(result.name, 'obsify'); 41 | t.end(); 42 | }); 43 | }); 44 | 45 | test.cb('module support', t => { 46 | m(fs).readFile('package.json', 'utf8') 47 | .map(content => JSON.parse(content)) 48 | .subscribe(result => { 49 | t.is(result.name, 'obsify'); 50 | t.end(); 51 | }); 52 | }); 53 | 54 | test('module support - doesn\'t transform *Sync methods by default', t => { 55 | t.is(JSON.parse(m(fs).readFileSync('package.json')).name, 'obsify'); 56 | }); 57 | 58 | test('module support - preserves non-function members', t => { 59 | const module = { 60 | method: function () {}, 61 | nonMethod: 3 62 | }; 63 | 64 | t.deepEqual(Object.keys(module), Object.keys(m(module))); 65 | }); 66 | 67 | test('module support - transforms only members in options.include', t => { 68 | const pModule = m(fixtureModule, { 69 | include: ['method1', 'method2'] 70 | }); 71 | 72 | t.is(typeof pModule.method1().subscribe, 'function'); 73 | t.is(typeof pModule.method2().subscribe, 'function'); 74 | t.not(typeof pModule.method3().subscribe, 'function'); 75 | }); 76 | 77 | test('module support - doesn\'t transform members in options.exclude', t => { 78 | const pModule = m(fixtureModule, { 79 | exclude: ['method3'] 80 | }); 81 | 82 | t.is(typeof pModule.method1().subscribe, 'function'); 83 | t.is(typeof pModule.method2().subscribe, 'function'); 84 | t.not(typeof pModule.method3().subscribe, 'function'); 85 | }); 86 | 87 | test('module support - options.include over options.exclude', t => { 88 | const pModule = m(fixtureModule, { 89 | include: ['method1', 'method2'], 90 | exclude: ['method2', 'method3'] 91 | }); 92 | 93 | t.is(typeof pModule.method1().subscribe, 'function'); 94 | t.is(typeof pModule.method2().subscribe, 'function'); 95 | t.not(typeof pModule.method3().subscribe, 'function'); 96 | }); 97 | 98 | test('module support — function modules', t => { 99 | const pModule = m(fixture4); 100 | 101 | t.is(typeof pModule().subscribe, 'function'); 102 | t.is(typeof pModule.meow().subscribe, 'function'); 103 | }); 104 | 105 | test('module support — function modules exclusion', t => { 106 | const pModule = m(fixture4, { 107 | excludeMain: true 108 | }); 109 | 110 | t.is(typeof pModule.meow().subscribe, 'function'); 111 | t.not(typeof pModule(function () {}).subscribe, 'function'); 112 | }); 113 | --------------------------------------------------------------------------------