custom-ability
Advanced tools
Comparing version 1.3.2 to 1.3.3
(function() { | ||
'use strict'; | ||
var defineProperty, extend, extendFilter, injectMethods, isArray, isBoolean, isFunction, | ||
var defineProperty, extend, extendFilter, getNonEnumNames, injectMethod, injectMethods, injectMethodsFromNonEnum, isArray, isBoolean, isFunction, | ||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; | ||
@@ -18,4 +18,32 @@ | ||
injectMethod = require('util-ex/lib/injectMethod'); | ||
defineProperty = require('util-ex/lib/defineProperty'); | ||
getNonEnumNames = require('util-ex/lib/get-non-enumerable-names'); | ||
injectMethodsFromNonEnum = function(aTargetClass, aObject, filter) { | ||
var nonEnumNames, result; | ||
nonEnumNames = getNonEnumNames(aObject); | ||
result = []; | ||
nonEnumNames.forEach(function(k) { | ||
var v; | ||
if (!filter || filter(k)) { | ||
if (k[0] === '$' && isFunction(v = aObject[k])) { | ||
k = k.substr(1); | ||
if (isFunction(aTargetClass[k])) { | ||
injectMethod(aTargetClass, k, v); | ||
} else if (aTargetClass[k] != null) { | ||
throw new TypeError('the same non-null name is not function:' + k); | ||
} else { | ||
aTargetClass[k] = v; | ||
} | ||
delete aObject[k]; | ||
result.push(k); | ||
} | ||
} | ||
}); | ||
return result; | ||
}; | ||
module.exports = function(abilityClass, aCoreMethod, isGetClassFunc) { | ||
@@ -51,4 +79,10 @@ var abilityFn, filter, getAdditionalAbility; | ||
if ((aOptions == null) || !(aOptions.include || aOptions.exclude)) { | ||
extend(aClass, AbilityClass); | ||
extend(aClass.prototype, AbilityClass.prototype); | ||
vExcludes = injectMethodsFromNonEnum(aClass, AbilityClass); | ||
extendFilter(aClass, AbilityClass, function(k) { | ||
return !(__indexOf.call(vExcludes, k) >= 0); | ||
}); | ||
vExcludes = injectMethodsFromNonEnum(aClass.prototype, AbilityClass.prototype); | ||
extend(aClass.prototype, AbilityClass.prototype, function(k) { | ||
return !(__indexOf.call(vExcludes, k) >= 0); | ||
}); | ||
} else { | ||
@@ -81,2 +115,6 @@ vIncludes = aOptions.include; | ||
}; | ||
vAbilities = injectMethodsFromNonEnum(aClass, AbilityClass, vFilter); | ||
vAbilities = vAbilities.concat(injectMethodsFromNonEnum(aClass.prototype, AbilityClass.prototype, vFilter)); | ||
vExcludes = vExcludes.concat(vAbilities); | ||
vAbilities = void 0; | ||
filterMethods = function(methods) { | ||
@@ -83,0 +121,0 @@ var k; |
{ | ||
"name": "custom-ability", | ||
"version": "1.3.2", | ||
"version": "1.3.3", | ||
"description": "make custom ability more easy. generate the ability which can be added to any class directly.", | ||
@@ -20,3 +20,3 @@ "homepage": "https://github.com/snowyu/custom-ability.js", | ||
"inherits-ex": "~1.0.6", | ||
"util-ex": "~0.3.2" | ||
"util-ex": "^0.3.8" | ||
}, | ||
@@ -23,0 +23,0 @@ "devDependencies": { |
306
README.md
@@ -11,2 +11,139 @@ ### custom-ability [![Build Status](https://img.shields.io/travis/snowyu/custom-ability.js/master.png)](http://travis-ci.org/snowyu/custom-ability.js) [![npm](https://img.shields.io/npm/v/custom-ability.svg)](https://npmjs.org/package/custom-ability) [![downloads](https://img.shields.io/npm/dm/custom-ability.svg)](https://npmjs.org/package/custom-ability) [![license](https://img.shields.io/npm/l/custom-ability.svg)](https://npmjs.org/package/custom-ability) | ||
## Usage | ||
suppose we wanna add the RefCount ability to any class directly. | ||
the RefCount ability will add the following members to your class. | ||
and you should implement the `destroy` method which will be called | ||
by `release`/`free`. | ||
* properties: | ||
* RefCount *(integer)*: the reference count. | ||
* methods: | ||
* `release()`/`free()`: Decrements reference count for this instance. | ||
If it is becoming less than 0, the object would be (self) destroyed. | ||
* `addRef()`: Increments the reference count for this instance | ||
and returns the new reference count. | ||
**Note**: The same name of the methods will be replaced via the ability. | ||
These old methods will be lost. So, you must confirm whether there are the | ||
same methods in your class before you apply the new ability. | ||
```coffee | ||
customAbility = require 'custom-ability' | ||
class RefCountable | ||
# define the instance methods here: | ||
release: -> | ||
result = --@RefCount | ||
@destroy() unless result >= 0 | ||
result | ||
free: @::release | ||
addRef: -> | ||
if not isUndefined @RefCount | ||
++@RefCount | ||
else | ||
@RefCount = 1 | ||
# the class methods if any: | ||
@someClassMethod: -> | ||
module.exports = customAbility RefCountable, 'addRef' | ||
``` | ||
do not forget to add the `"ability"` keyword to your package.json which means | ||
the ability power with it. | ||
```js | ||
// package.json | ||
"keywords": [ | ||
"ability", | ||
... | ||
], | ||
``` | ||
do not forget to add the `"ability.js"` file on your package root folder too. | ||
now user use this ability like this: | ||
```coffee | ||
refable = require 'ref-object/ability' | ||
class MyClass | ||
refable MyClass | ||
destroy: ->console.log 'destroy' | ||
my = new MyClass | ||
my.addRef() | ||
my.free() # nothing | ||
my.free() # print the 'destroy' here. | ||
``` | ||
More complicated example, you can see the [events-ex/src/eventable.coffee](https://github.com/snowyu/events-ex.js). | ||
## additional $abilities | ||
In order to make certain ability to work, you need to modify some methods | ||
of the class. this time we need the "additional abilities" now. eg, the | ||
event-able ability to [AbstractObject](https://github.com/snowyu/abstract-object). | ||
We need to send a notification event when the state of the object changes. | ||
So the event-able of [AbstractObject](https://github.com/snowyu/abstract-object) | ||
should be: | ||
```coffee | ||
eventable = require 'events-ex/eventable' | ||
eventableOptions = require './eventable-options' | ||
module.exports = (aClass, aOptions)-> | ||
eventable aClass, eventableOptions(aOptions) | ||
``` | ||
```coffee | ||
# eventable-options.coffee | ||
module.exports = (aOptions)-> | ||
aOptions = {} unless aOptions | ||
aOptions.methods = {} unless aOptions.methods | ||
extend aOptions.methods, | ||
# override methods: | ||
setObjectState: (value, emitted = true)-> | ||
self= @self | ||
@super.call(self, value) | ||
self.emit value, self if emitted | ||
return | ||
... | ||
return aOptions | ||
# more detail on [AbstractObject/src/eventable-options.coffee](https://github.com/snowyu/abstract-object) | ||
``` | ||
the original `eventable('events-ex/eventable')` is no useful for AbstractObject. | ||
but we wanna the original `eventable('events-ex/eventable')` knows the changes | ||
and use it automatically. | ||
```coffee | ||
eventable = require 'events-ex/eventable' | ||
class MyClass | ||
inherits MyClass, AbstractObject | ||
eventable MyClass | ||
``` | ||
you just do this on the AbstractObject: | ||
```coffee | ||
AbstractObject = require('./lib/abstract-object') | ||
AbstractObject.$abilities = | ||
# "Eventable" is the AbilityClass name | ||
Eventable: require('./lib/eventable-options') | ||
module.exports = AbstractObject | ||
``` | ||
# API | ||
@@ -20,3 +157,3 @@ | ||
* customAbility(abilityClass[, coreMethod[, isGetClassFunction]]) | ||
## customAbility(abilityClass[, coreMethod[, isGetClassFunction]]) | ||
@@ -41,3 +178,3 @@ __arguments__ | ||
* note: the `coreMethod` could not be excluded. It's always added to the class. | ||
* `methods `*(object)*: hooked methods to the class | ||
* `methods `*(object)*: injected/hooked methods to the class | ||
* key: the method name to hook. | ||
@@ -47,8 +184,37 @@ * value: the new method function, if original method is exists or not in replacedMethods: | ||
* `this.self` is the original `this` object. | ||
* `classMethods` *(object)*: hooked class methods to the class | ||
* `classMethods` *(object)*: hooked class methods to the class, it's the same as the `methods`. | ||
* `replacedMethods` *(array)*: the method name in the array will be replaced the original | ||
method directly. | ||
# Specification | ||
## V1.3.3 | ||
+ use the injectMethods(AOP) for the methods of non-enumerable and beginning with '$' in an ability | ||
to call `super` method. | ||
```coffee | ||
customAbility = require 'custom-ability' | ||
class PropertyManagerAbility | ||
constructor: ->@initialize.call @, arguments[gOptPos] | ||
# the non-enumerable property and beginning with '$' will | ||
# be injected to `initialize` method | ||
defineProperty @::, '$initialize', -> | ||
options = arugments[gOptPos] | ||
options?={} | ||
that = @ | ||
if @super and @self | ||
inherited = @super | ||
that = @self | ||
inherited.apply(that, arugments) | ||
that._initialize options if isFunction that._initialize | ||
that.defineProperties(options.attributes) | ||
that.assign(options) | ||
module.exports = customAbility PropertyManagerAbility, 'assign' | ||
``` | ||
## V1.3.x | ||
@@ -154,135 +320,1 @@ | ||
``` | ||
## Usage | ||
suppose we wanna add the RefCount ability to any class directly. | ||
the RefCount ability will add the following members to your class. | ||
and you should implement the `destroy` method which will be called | ||
by `release`/`free`. | ||
* properties: | ||
* RefCount *(integer)*: the reference count. | ||
* methods: | ||
* `release()`/`free()`: Decrements reference count for this instance. | ||
If it is becoming less than 0, the object would be (self) destroyed. | ||
* `addRef()`: Increments the reference count for this instance | ||
and returns the new reference count. | ||
```coffee | ||
customAbility = require 'custom-ability' | ||
class RefCountable | ||
# define the instance methods here: | ||
release: -> | ||
result = --@RefCount | ||
@destroy() unless result >= 0 | ||
result | ||
free: @::release | ||
addRef: -> | ||
if not isUndefined @RefCount | ||
++@RefCount | ||
else | ||
@RefCount = 1 | ||
# the class methods if any: | ||
@someClassMethod: -> | ||
module.exports = customAbility RefCountable, 'addRef' | ||
``` | ||
do not forget to add the `"ability"` keyword to your package.json which means | ||
the ability power with it. | ||
```js | ||
// package.json | ||
"keywords": [ | ||
"ability", | ||
... | ||
], | ||
``` | ||
do not forget to add the `"ability.js"` file on your package root folder too. | ||
now user use this ability like this: | ||
```coffee | ||
refable = require 'ref-object/ability' | ||
class MyClass | ||
refable MyClass | ||
destroy: ->console.log 'destroy' | ||
my = new MyClass | ||
my.addRef() | ||
my.free() # nothing | ||
my.free() # print the 'destroy' here. | ||
``` | ||
More complicated example, you can see the [events-ex/src/eventable.coffee](https://github.com/snowyu/events-ex.js). | ||
## additional $abilities | ||
In order to make certain ability to work, you need to modify some methods | ||
of the class. this time we need the "additional abilities" now. eg, the | ||
event-able ability to [AbstractObject](https://github.com/snowyu/abstract-object). | ||
We need to send a notification event when the state of the object changes. | ||
So the event-able of [AbstractObject](https://github.com/snowyu/abstract-object) | ||
should be: | ||
```coffee | ||
eventable = require 'events-ex/eventable' | ||
eventableOptions = require './eventable-options' | ||
module.exports = (aClass, aOptions)-> | ||
eventable aClass, eventableOptions(aOptions) | ||
``` | ||
```coffee | ||
# eventable-options.coffee | ||
module.exports = (aOptions)-> | ||
aOptions = {} unless aOptions | ||
aOptions.methods = {} unless aOptions.methods | ||
extend aOptions.methods, | ||
# override methods: | ||
setObjectState: (value, emitted = true)-> | ||
self= @self | ||
@super.call(self, value) | ||
self.emit value, self if emitted | ||
return | ||
... | ||
return aOptions | ||
# more detail on [AbstractObject/src/eventable-options.coffee](https://github.com/snowyu/abstract-object) | ||
``` | ||
the original `eventable('events-ex/eventable')` is no useful for AbstractObject. | ||
but we wanna the original `eventable('events-ex/eventable')` knows the changes | ||
and use it automatically. | ||
```coffee | ||
eventable = require 'events-ex/eventable' | ||
class MyClass | ||
inherits MyClass, AbstractObject | ||
eventable MyClass | ||
``` | ||
you just do this on the AbstractObject: | ||
```coffee | ||
AbstractObject = require('./lib/abstract-object') | ||
AbstractObject.$abilities = | ||
# "Eventable" is the AbilityClass name | ||
Eventable: require('./lib/eventable-options') | ||
module.exports = AbstractObject | ||
``` |
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
43565
214
316
Updatedutil-ex@^0.3.8