├── .babelrc ├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── package.json ├── src └── index.js └── test └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins": [ 6 | "add-module-exports" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.1" 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://img.shields.io/travis/markdalgleish/instrument-methods/master.svg?style=flat-square)](http://travis-ci.org/markdalgleish/instrument-methods) [![npm](https://img.shields.io/npm/v/instrument-methods.svg?style=flat-square)](https://www.npmjs.com/package/instrument-methods) 2 | 3 | # instrument-methods 4 | 5 | Simple object method instrumentation. 6 | 7 | ```bash 8 | $ npm install --save instrument-methods 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```js 14 | import instrumentMethods from 'instrument-methods'; 15 | 16 | const instance = { 17 | doThis: () => { ... }, 18 | doThat: () => { ... } 19 | }; 20 | 21 | instrumentMethods(instance, { 22 | before: (methodName, args) => console.log(`"${methodName}" is about to be called!`, ...args), 23 | after: (methodName, argS) => console.log(`"${methodName}" was called!`, ...args) 24 | }); 25 | 26 | instance.doThat(1, 2, 3); 27 | 28 | // Logs: 29 | // "doThat" is about to be called!, 1, 2, 3 30 | // "doThat" was called!, 1, 2, 3 31 | ``` 32 | 33 | Even though it modifies the object, `instrumentMethods` also returns a reference to the modified object so it can be used as part of an expression: 34 | 35 | ```js 36 | const after = methodName => console.log(`"${methodName}" was called!`); 37 | 38 | const instance = instrumentMethods({ 39 | doThis: () => { ... }, 40 | doThat: () => { ... } 41 | }, { after }); 42 | ``` 43 | 44 | ## License 45 | 46 | [MIT License](http://markdalgleish.mit-license.org/) 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "instrument-methods", 3 | "version": "1.1.1", 4 | "description": "Simple object method instrumentation", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "test": "mocha --compilers js:babel-register", 8 | "build": "rm -rf dist && mkdir dist && ./node_modules/.bin/babel -d dist/ src/", 9 | "prepublish": "npm run build" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/markdalgleish/instrument-methods.git" 14 | }, 15 | "author": "Mark Dalgleish", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/markdalgleish/instrument-methods/issues" 19 | }, 20 | "homepage": "https://github.com/markdalgleish/instrument-methods#readme", 21 | "devDependencies": { 22 | "babel-cli": "^6.5.1", 23 | "babel-plugin-add-module-exports": "^0.1.2", 24 | "babel-preset-es2015": "^6.5.0", 25 | "babel-register": "^6.5.2", 26 | "chai": "^3.5.0", 27 | "mocha": "^2.4.5", 28 | "sinon": "^1.17.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const noop = () => {}; 2 | 3 | export default (instance, { before = noop, after = noop }) => { 4 | Object.keys(instance).forEach(methodName => { 5 | if (typeof instance[methodName] === 'function') { 6 | const originalMethod = instance[methodName]; 7 | 8 | instance[methodName] = function() { 9 | before(methodName, arguments); 10 | const returnVal = originalMethod.apply(this, arguments); 11 | after(methodName, arguments); 12 | 13 | return returnVal; 14 | }; 15 | } 16 | }); 17 | 18 | return instance; 19 | }; 20 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import instrumentMethods from '../src'; 2 | import { spy, stub } from 'sinon'; 3 | import { expect } from 'chai'; 4 | 5 | describe('instrument-methods', () => { 6 | ['before', 'after'].forEach(position => { 7 | describe(`Applying instrumentation ${position} each method`, () => { 8 | let instrumentationSpy; 9 | let methodStub; 10 | let methodReturnVal; 11 | let object; 12 | let returnVal; 13 | 14 | beforeEach(() => { 15 | instrumentationSpy = spy(); 16 | methodStub = stub().returns('method return value'); 17 | object = { method: methodStub }; 18 | returnVal = instrumentMethods(object, { [position]: instrumentationSpy }); 19 | }); 20 | 21 | it('Should return a reference to the original object', () => { 22 | expect(returnVal).to.equal(object); 23 | }); 24 | 25 | describe('When a method is executed', () => { 26 | beforeEach(() => methodReturnVal = object.method(1, 2, 3)); 27 | 28 | it('Should call the provided function with the methodName', () => { 29 | expect(instrumentationSpy.getCall(0).args[0]).to.equal('method'); 30 | }); 31 | 32 | it('Should call the provided function with the arguments', () => { 33 | expect(Array.from(instrumentationSpy.getCall(0).args[1])).to.deep.equal([1, 2, 3]); 34 | }); 35 | 36 | it('Should call the original method with correct context', () => { 37 | expect(methodStub.getCall(0).thisValue).to.equal(object); 38 | }); 39 | 40 | it('Should call the original method with the correct arguments', () => { 41 | expect(methodStub.getCall(0).args).to.deep.equal([1, 2, 3]); 42 | }); 43 | 44 | it('Should forward the return value from the original method', () => { 45 | expect(methodReturnVal).to.equal('method return value'); 46 | }); 47 | }); 48 | 49 | describe('When a method is executed with a custom context via "call"', () => { 50 | beforeEach(() => object.method.call('custom context', 4, 5, 6)); 51 | 52 | it('Should support dynamic method context', () => { 53 | expect(methodStub.getCall(0).thisValue).to.equal('custom context'); 54 | }); 55 | 56 | it('Should call the original method with the correct arguments', () => { 57 | expect(methodStub.getCall(0).args).to.deep.equal([4, 5, 6]); 58 | }); 59 | }); 60 | }); 61 | }); 62 | }); 63 | --------------------------------------------------------------------------------