+76
-42
@@ -6,7 +6,15 @@ /* vim:set ts=2 sw=2 sts=2 expandtab */ | ||
| var Name = require('name') | ||
| // Shortcuts for ES5 reflection functions. | ||
| var create = Object.create | ||
| var defineProperty = Object.defineProperty | ||
| var make = Object.create || (function() { | ||
| var Type = function Type() {} | ||
| return function make(prototype) { | ||
| Type.prototype = prototype | ||
| return new Type() | ||
| } | ||
| }) | ||
| var defineProperty = Object.defineProperty || function(object, name, property) { | ||
| object[name] = property.value | ||
| return object | ||
| } | ||
| var typefy = Object.prototype.toString | ||
@@ -22,3 +30,3 @@ function Method(base) { | ||
| // use it's name as a name hint. | ||
| var name = Name(base && base.name).toString() | ||
| var name = (base && base.name || "") + Math.random().toString(32).substr(2) | ||
@@ -28,17 +36,20 @@ function dispatch() { | ||
| var target = arguments[0] | ||
| var builtin = null | ||
| // If first argument is `null` or `undefined` use associated property | ||
| // maps for implementation lookups, otherwise use first argument itself. | ||
| // Use default implementation lookup map if first argument does not | ||
| // implements Method itself. | ||
| var implementation = target === null ? Null[name] : | ||
| target === undefined ? Undefined[name] : | ||
| target[name] || Default[name] | ||
| // maps for implementation lookups, otherwise attempt to use implementation | ||
| // for built-in falling back for implementation on the first argument. | ||
| // Finally use default implementation if no other one is found. | ||
| var method = target === null ? Null[name] : | ||
| target === undefined ? Undefined[name] : | ||
| target[name] || | ||
| ((builtin = Builtins[typefy.call(target)]) && builtin[name]) || | ||
| Builtins.Object[name] || | ||
| Default[name] | ||
| // If implementation not found there's not much we can do about it, | ||
| // throw error with a descriptive message. | ||
| if (!implementation) | ||
| throw Error('Type does not implements Method') | ||
| if (!method) throw Error('Type does not implements method') | ||
| // If implementation is found delegate to it. | ||
| return implementation.apply(implementation, arguments) | ||
| return method.apply(method, arguments) | ||
| } | ||
@@ -53,3 +64,3 @@ | ||
| // object[foo] = function() { /***/ } | ||
| dispatch.toString = function() { return name } | ||
| dispatch.toString = function toString() { return name } | ||
@@ -63,3 +74,26 @@ // Copy utility Methods for convenient API. | ||
| function implement(object, Method, implementation) { | ||
| // Define objects where Methods implementations for `null`, `undefined` and | ||
| // defaults will be stored. | ||
| var Default = {} | ||
| var Null = make(Default) | ||
| var Undefined = make(Default) | ||
| // Implementation for built-in types are stored in the hash, this avoids | ||
| // mutations on built-ins and allows cross frame extensions. Primitive types | ||
| // are predefined so that `Object` extensions won't be inherited. | ||
| var Builtins = { | ||
| Object: make(Default), | ||
| Number: make(Default), | ||
| String: make(Default), | ||
| Boolean: make(Default) | ||
| } | ||
| // Define aliases for predefined built-in maps to a forms that values will | ||
| // be serialized on dispatch. | ||
| Builtins[typefy.call(Object.prototype)] = Builtins.Object | ||
| Builtins[typefy.call(Number.prototype)] = Builtins.Number | ||
| Builtins[typefy.call(String.prototype)] = Builtins.String | ||
| var implement = Method( | ||
| function implement(method, object, lambda) { | ||
| /** | ||
@@ -74,6 +108,11 @@ Implements `Method` for the given `object` with a provided `implementation`. | ||
| return defineProperty(target, Method.toString(), { value: implementation }) | ||
| } | ||
| return defineProperty(target, method.toString(), { | ||
| enumerable: false, | ||
| configurable: false, | ||
| writable: false, | ||
| value: lambda | ||
| }) | ||
| }) | ||
| function define(Type, Method, implementation) { | ||
| var define = Method(function define(method, Type, lambda) { | ||
| /** | ||
@@ -86,28 +125,21 @@ Defines `Method` for the given `Type` with a provided `implementation`. | ||
| **/ | ||
| return implement(Type && Type.prototype, Method, implementation) | ||
| } | ||
| Method.prototype = create(null, { | ||
| toString: { value: Object.prototype.toString }, | ||
| valueOf: { value: Object.prototype.valueOf }, | ||
| define: { value: function(Type, implementation) { | ||
| return define(Type, this, implementation) | ||
| }}, | ||
| implement: { value: function(object, implementation) { | ||
| return implement(object, this, implementation) | ||
| }} | ||
| if (!lambda) return implement(method, Default, Type) | ||
| if (!Type) return implement(method, Type, lambda) | ||
| var type = typefy.call(Type.prototype) | ||
| return type !== "[object Object]" ? implement(method, | ||
| Builtins[type] || (Builtins[type] = make(Builtins.Object)), lambda) : | ||
| // This should be `Type === Object` but since it will be `false` for | ||
| // `Object` from different JS context / compartment / frame we assume that | ||
| // if it's name is `Object` it is Object. | ||
| Type.name === "Object" ? implement(method, Builtins.Object, lambda) : | ||
| implement(method, Type.prototype, lambda) | ||
| }) | ||
| // Define objects where Methods implementations for `null`, `undefined` and | ||
| // defaults will be stored. Note that we create these objects from `null`, | ||
| // otherwise implementation from `Object` would have being inherited. Also | ||
| // notice that `Default` implementations are stored on `Method.prototype` this | ||
| // provides convenient way for defining default implementations. | ||
| var Default = Method.prototype | ||
| var Null = create(Default) | ||
| var Undefined = create(Default) | ||
| var defineMethod = function defineMethod(Type, lambda) { | ||
| return define(this, Type, lambda) | ||
| } | ||
| var implementMethod = function implementMethod(object, lambda) { | ||
| return implement(this, object, lambda) | ||
| } | ||
| // Create Method shortcuts as for a faster access. | ||
| var defineMethod = Default.define | ||
| var implementMethod = Default.implement | ||
@@ -120,3 +152,5 @@ // Define exports on `Method` as it's only thing we export. | ||
| Method.Undefined = Undefined | ||
| Method.Default = Default | ||
| Method.Builtins = Builtins | ||
| module.exports = Method |
+25
-0
| # Changes | ||
| ## 0.1.1 / 2012-10-15 | ||
| - Fix regression causing custom type implementation to be stored on objects. | ||
| ## 0.1.0 / 2012-10-15 | ||
| - Remove dependency on name module. | ||
| - Implement fallback for engines that do not support ES5. | ||
| - Add support for built-in type extensions without extending their prototypes. | ||
| - Make API for default definitions more intuitive. | ||
| Skipping type argument now defines default: | ||
| isFoo.define(function(value) { | ||
| return false | ||
| }) | ||
| - Make exposed `define` and `implement` polymorphic. | ||
| - Removed dev dependency on swank-js. | ||
| - Primitive types `string, number, boolean` no longer inherit method | ||
| implementations from `Object`. | ||
| ## 0.0.3 / 2012-07-17 | ||
| - Remove module boilerplate | ||
| ## 0.0.2 / 2012-06-26 | ||
@@ -4,0 +29,0 @@ |
+2
-6
| { | ||
| "name": "method", | ||
| "id": "method", | ||
| "version": "0.0.3", | ||
| "version": "0.1.1", | ||
| "description": "Functional polymorphic method dispatch", | ||
@@ -18,9 +18,5 @@ "keywords": [ "method", "dispatch", "protocol", "polymorphism", "type dispatch" ], | ||
| }, | ||
| "dependencies": { | ||
| "name": ">=0.0.1" | ||
| }, | ||
| "devDependencies": { | ||
| "test": ">=0.4.4", | ||
| "repl-utils": ">=1.0.0", | ||
| "swank-js": ">=0.0.1" | ||
| "repl-utils": ">=1.0.0" | ||
| }, | ||
@@ -27,0 +23,0 @@ "scripts": { |
+1
-1
@@ -59,3 +59,3 @@ # method | ||
| // Or simply define default implementation: | ||
| isWatchable.define(Method, function() { return false }) | ||
| isWatchable.define(function() { return false }) | ||
@@ -62,0 +62,0 @@ // Alternatively default implementation may be provided at creation: |
+49
-43
@@ -65,3 +65,3 @@ /* vim:set ts=2 sw=2 sts=2 expandtab */ | ||
| var isImplemented = Method() | ||
| isImplemented.define(Method, function() { | ||
| isImplemented.define(function() { | ||
| return true | ||
@@ -76,29 +76,2 @@ }) | ||
| exports['-test method for Object'] = function(assert) { | ||
| var isObject = Method() | ||
| var expected = trues.slice() | ||
| isObject.define(Object, function() { return true }) | ||
| assert.throws(function() { | ||
| isObject(null) | ||
| }, /not implement/i, 'not implemented for null') | ||
| assert.throws(function() { | ||
| isObject(undefined) | ||
| }, /not implement/i, 'not implemented for undefined') | ||
| isObject.define(Method, function() { return false }) | ||
| expected = [ false, false ].concat(expected.slice(2)) | ||
| assert.deepEqual(values.map(isObject), | ||
| expected, | ||
| 'null and undefined inherits default implementation ' + | ||
| 'rest from Object') | ||
| isObject.define(Function, function() { return false }) | ||
| expected = expected.slice(0, 11).concat(false, false).concat(expected.slice(13)) | ||
| assert.deepEqual(values.map(isObject), expected, 'functions inherit' + | ||
| 'implementations from functions'); | ||
| } | ||
| exports['test dispatch not-implemented'] = function(assert) { | ||
@@ -117,3 +90,3 @@ var isDefault = Method() | ||
| // Implement default | ||
| isDefault.define(Method, True) | ||
| isDefault.define(True) | ||
| assert.deepEqual(values.map(isDefault), trues, | ||
@@ -128,3 +101,3 @@ 'all implementation inherit from default') | ||
| // Implement default | ||
| isNull.define(Method, False) | ||
| isNull.define(False) | ||
| isNull.define(null, True) | ||
@@ -141,3 +114,3 @@ assert.deepEqual(values.map(isNull), | ||
| // Implement default | ||
| isUndefined.define(Method, False) | ||
| isUndefined.define(False) | ||
| isUndefined.define(undefined, True) | ||
@@ -154,11 +127,9 @@ assert.deepEqual(values.map(isUndefined), | ||
| // Implement default | ||
| isObject.define(Method, False) | ||
| isObject.define(False) | ||
| isObject.define(Object, True) | ||
| assert.deepEqual(values.map(isObject), | ||
| [ false, false ]. | ||
| concat(trues.slice(2, 7)). | ||
| concat(false). | ||
| concat(trues.slice(8)), | ||
| 'all values except null, undefined, Object.create(null) ' + | ||
| 'inherit from Object') | ||
| [ false, false, false, false, false ]. | ||
| concat(trues.slice(5, 15)). | ||
| concat([false]), | ||
| 'all values except primitives inherit Object methods') | ||
@@ -169,3 +140,3 @@ } | ||
| var isNumber = Method() | ||
| isNumber.define(Method, False) | ||
| isNumber.define(False) | ||
| isNumber.define(Number, True) | ||
@@ -182,3 +153,3 @@ | ||
| var isString = Method() | ||
| isString.define(Method, False) | ||
| isString.define(False) | ||
| isString.define(String, True) | ||
@@ -194,3 +165,3 @@ | ||
| var isFunction = Method() | ||
| isFunction.define(Method, False) | ||
| isFunction.define(False) | ||
| isFunction.define(Function, True) | ||
@@ -208,3 +179,3 @@ | ||
| var isDate = Method() | ||
| isDate.define(Method, False) | ||
| isDate.define(False) | ||
| isDate.define(Date, True) | ||
@@ -222,3 +193,3 @@ | ||
| var isRegExp = Method() | ||
| isRegExp.define(Method, False) | ||
| isRegExp.define(False) | ||
| isRegExp.define(RegExp, True) | ||
@@ -247,2 +218,37 @@ | ||
| exports['test redefine for descendant'] = function(assert) { | ||
| var isFoo = Method() | ||
| var ancestor = {} | ||
| isFoo.implement(ancestor, function() { return true }) | ||
| var descendant = Object.create(ancestor) | ||
| isFoo.implement(descendant, function() { return false }) | ||
| assert.ok(isFoo(ancestor), 'defined on ancestor') | ||
| assert.ok(!isFoo(descendant), 'overrided for descendant') | ||
| } | ||
| exports['test on custom types'] = function(assert) { | ||
| function Foo() {} | ||
| var isFoo = Method() | ||
| isFoo.define(function() { return false }) | ||
| isFoo.define(Foo, function() { return true }) | ||
| assert.ok(!isFoo({}), "object is get's default implementation") | ||
| assert.ok(isFoo(new Foo()), "Foo type objects get own implementation") | ||
| var isObject = Method() | ||
| isObject.define(function() { return false }) | ||
| isObject.define(Object, function() { return true }) | ||
| assert.ok(isObject(new Foo()), "foo inherits implementation from object") | ||
| isObject.define(Foo, function() { return false }) | ||
| assert.ok(!isObject(new Foo()), | ||
| "implementation inherited form object can be overrided") | ||
| } | ||
| if (require.main === module) require('test').run(exports) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
1138880
14.02%0
-100%2
-33.33%318
11.97%1
Infinity%- Removed
- Removed