michael@0: # method michael@0: michael@0: [![Build Status](https://secure.travis-ci.org/Gozala/method.png)](http://travis-ci.org/Gozala/method) michael@0: michael@0: Library provides an API for defining polymorphic methods that dispatch on the michael@0: first argument type. This provides a powerful way for decouple abstraction michael@0: interface definition from an actual implementation per type, without risks michael@0: of interference with other libraries. michael@0: michael@0: ### Motivation michael@0: michael@0: - Provide a high-performance, dynamic polymorphism construct as an michael@0: alternative to existing object methods that does not provides any michael@0: mechanics for guarding against name conflicts. michael@0: - Allow independent extension of types, and implementations of methods michael@0: on types, by different parties. michael@0: michael@0: ## Install michael@0: michael@0: npm install method michael@0: michael@0: ## Use michael@0: michael@0: ```js michael@0: var method = require("method") michael@0: michael@0: // Define `isWatchable` method that can be implemented for any type. michael@0: var isWatchable = method("isWatchable") michael@0: michael@0: // If you call it on any object it will michael@0: // throw as nothing implements that method yet. michael@0: //isWatchable({}) // => Exception: method is not implemented michael@0: michael@0: // If you define private method on `Object.prototype` michael@0: // all objects will inherit it. michael@0: Object.prototype[isWatchable] = function() { michael@0: return false; michael@0: } michael@0: michael@0: isWatchable({}) // => false michael@0: michael@0: michael@0: // Although `isWatchable` property above will be enumerable and there for michael@0: // may damage some assumbtions made by other libraries. There for it"s michael@0: // recomended to use built-in helpers methods that will define extension michael@0: // without breaking assumbtions made by other libraries: michael@0: michael@0: isWatchable.define(Object, function() { return false }) michael@0: michael@0: michael@0: // There are primitive types in JS that won"t inherit methods from Object: michael@0: isWatchable(null) // => Exception: method is not implemented michael@0: michael@0: // One could either implement methods for such types: michael@0: isWatchable.define(null, function() { return false }) michael@0: isWatchable.define(undefined, function() { return false }) michael@0: michael@0: // Or simply define default implementation: michael@0: isWatchable.define(function() { return false }) michael@0: michael@0: // Alternatively default implementation may be provided at creation: michael@0: isWatchable = method(function() { return false }) michael@0: michael@0: // Method dispatches on an first argument type. That allows us to create michael@0: // new types with an alternative implementations: michael@0: function Watchable() {} michael@0: isWatchable.define(Watchable, function() { return true }) michael@0: michael@0: // This will make all `Watchable` instances watchable! michael@0: isWatchable(new Watchable()) // => true michael@0: michael@0: // Arbitrary objects can also be extended to implement given method. For example michael@0: // any object can simply made watchable: michael@0: function watchable(object) { michael@0: return isWatchable.implement(objct, function() { return true }) michael@0: } michael@0: michael@0: isWatchable(watchable({})) // => true michael@0: michael@0: // Full protocols can be defined with such methods: michael@0: var observers = "observers@" + module.filename michael@0: var watchers = method("watchers") michael@0: var watch = method("watch") michael@0: var unwatch = method("unwatch") michael@0: michael@0: watchers.define(Watchable, function(target) { michael@0: return target[observers] || (target[observers] = []) michael@0: }) michael@0: michael@0: watch.define(Watchable, function(target, watcher) { michael@0: var observers = watchers(target) michael@0: if (observers.indexOf(watcher) < 0) observers.push(watcher) michael@0: return target michael@0: }) michael@0: unwatch.define(Watchable, function(target, watcher) { michael@0: var observers = watchers(target) michael@0: var index = observers.indexOf(watcher) michael@0: if (observers.indexOf(watcher) >= 0) observers.unshift(watcher) michael@0: return target michael@0: }) michael@0: michael@0: // Define type Port that inherits form Watchable michael@0: michael@0: function Port() {} michael@0: Port.prototype = Object.create(Watchable.prototype) michael@0: michael@0: var emit = method("emit") michael@0: emit.define(Port, function(port, message) { michael@0: watchers(port).slice().forEach(function(watcher) { michael@0: watcher(message) michael@0: }) michael@0: }) michael@0: michael@0: var p = new Port() michael@0: watch(p, console.log) michael@0: emit(p, "hello world") // => info: "hello world" michael@0: ```