├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .verb.md ├── LICENSE ├── README.md ├── example.js ├── examples ├── a.js ├── app.js ├── b.js ├── c.js └── meta.js ├── fixtures └── app.js ├── gulpfile.js ├── index.js ├── package.json └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [{**/{actual,fixtures,expected,templates}/**,*.md}] 13 | trim_trailing_whitespace = false 14 | insert_final_newline = false 15 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "es6": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | 9 | "globals": { 10 | "document": false, 11 | "navigator": false, 12 | "window": false 13 | }, 14 | 15 | "rules": { 16 | "accessor-pairs": 2, 17 | "arrow-spacing": [2, { "before": true, "after": true }], 18 | "block-spacing": [2, "always"], 19 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 20 | "comma-dangle": [2, "never"], 21 | "comma-spacing": [2, { "before": false, "after": true }], 22 | "comma-style": [2, "last"], 23 | "constructor-super": 2, 24 | "curly": [2, "multi-line"], 25 | "dot-location": [2, "property"], 26 | "eol-last": 2, 27 | "eqeqeq": [2, "allow-null"], 28 | "generator-star-spacing": [2, { "before": true, "after": true }], 29 | "handle-callback-err": [2, "^(err|error)$" ], 30 | "indent": [2, 2, { "SwitchCase": 1 }], 31 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 32 | "keyword-spacing": [2, { "before": true, "after": true }], 33 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 34 | "new-parens": 2, 35 | "no-array-constructor": 2, 36 | "no-caller": 2, 37 | "no-class-assign": 2, 38 | "no-cond-assign": 2, 39 | "no-const-assign": 2, 40 | "no-control-regex": 2, 41 | "no-debugger": 2, 42 | "no-delete-var": 2, 43 | "no-dupe-args": 2, 44 | "no-dupe-class-members": 2, 45 | "no-dupe-keys": 2, 46 | "no-duplicate-case": 2, 47 | "no-empty-character-class": 2, 48 | "no-eval": 2, 49 | "no-ex-assign": 2, 50 | "no-extend-native": 2, 51 | "no-extra-bind": 2, 52 | "no-extra-boolean-cast": 2, 53 | "no-extra-parens": [2, "functions"], 54 | "no-fallthrough": 2, 55 | "no-floating-decimal": 2, 56 | "no-func-assign": 2, 57 | "no-implied-eval": 2, 58 | "no-inner-declarations": [2, "functions"], 59 | "no-invalid-regexp": 2, 60 | "no-irregular-whitespace": 2, 61 | "no-iterator": 2, 62 | "no-label-var": 2, 63 | "no-labels": 2, 64 | "no-lone-blocks": 2, 65 | "no-mixed-spaces-and-tabs": 2, 66 | "no-multi-spaces": 2, 67 | "no-multi-str": 2, 68 | "no-multiple-empty-lines": [2, { "max": 1 }], 69 | "no-native-reassign": 0, 70 | "no-negated-in-lhs": 2, 71 | "no-new": 2, 72 | "no-new-func": 2, 73 | "no-new-object": 2, 74 | "no-new-require": 2, 75 | "no-new-wrappers": 2, 76 | "no-obj-calls": 2, 77 | "no-octal": 2, 78 | "no-octal-escape": 2, 79 | "no-proto": 0, 80 | "no-redeclare": 2, 81 | "no-regex-spaces": 2, 82 | "no-return-assign": 2, 83 | "no-self-compare": 2, 84 | "no-sequences": 2, 85 | "no-shadow-restricted-names": 2, 86 | "no-spaced-func": 2, 87 | "no-sparse-arrays": 2, 88 | "no-this-before-super": 2, 89 | "no-throw-literal": 2, 90 | "no-trailing-spaces": 0, 91 | "no-undef": 2, 92 | "no-undef-init": 2, 93 | "no-unexpected-multiline": 2, 94 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 95 | "no-unreachable": 2, 96 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 97 | "no-useless-call": 0, 98 | "no-with": 2, 99 | "one-var": [0, { "initialized": "never" }], 100 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], 101 | "padded-blocks": [0, "never"], 102 | "quotes": [2, "single", "avoid-escape"], 103 | "radix": 2, 104 | "semi": [2, "always"], 105 | "semi-spacing": [2, { "before": false, "after": true }], 106 | "space-before-blocks": [2, "always"], 107 | "space-before-function-paren": [2, "never"], 108 | "space-in-parens": [2, "never"], 109 | "space-infix-ops": 2, 110 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 111 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], 112 | "use-isnan": 2, 113 | "valid-typeof": 2, 114 | "wrap-iife": [2, "any"], 115 | "yoda": [2, "never"] 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text eol=lf 3 | 4 | # binaries 5 | *.ai binary 6 | *.psd binary 7 | *.jpg binary 8 | *.gif binary 9 | *.png binary 10 | *.jpeg binary 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # always ignore files 2 | *.DS_Store 3 | .idea 4 | .vscode 5 | *.sublime-* 6 | 7 | # test related, or directories generated by tests 8 | test/actual 9 | actual 10 | coverage 11 | .nyc* 12 | 13 | # npm 14 | node_modules 15 | npm-debug.log 16 | 17 | # yarn 18 | yarn.lock 19 | yarn-error.log 20 | 21 | # misc 22 | _gh_pages 23 | _draft 24 | _drafts 25 | bower_components 26 | vendor 27 | temp 28 | tmp 29 | TODO.md 30 | package-lock.json -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | language: node_js 6 | node_js: 7 | - node 8 | - '9' 9 | - '8' 10 | - '7' 11 | - '6' 12 | - '5' 13 | - '4' 14 | - '0.12' 15 | - '0.10' 16 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```js 4 | var cu = require('{%= name %}'); 5 | ``` 6 | 7 | ## API 8 | {%= apidocs('index.js') %} 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015, 2017-2018, Jon Schlinkert. 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 | # class-utils [![NPM version](https://img.shields.io/npm/v/class-utils.svg?style=flat)](https://www.npmjs.com/package/class-utils) [![NPM monthly downloads](https://img.shields.io/npm/dm/class-utils.svg?style=flat)](https://npmjs.org/package/class-utils) [![NPM total downloads](https://img.shields.io/npm/dt/class-utils.svg?style=flat)](https://npmjs.org/package/class-utils) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/class-utils.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/class-utils) 2 | 3 | > Utils for working with JavaScript classes and prototype methods. 4 | 5 | Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support. 6 | 7 | ## Install 8 | 9 | Install with [npm](https://www.npmjs.com/): 10 | 11 | ```sh 12 | $ npm install --save class-utils 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```js 18 | var cu = require('class-utils'); 19 | ``` 20 | 21 | ## API 22 | 23 | ### [.has](index.js#L43) 24 | 25 | Returns true if an array has any of the given elements, or an object has any of the give keys. 26 | 27 | **Params** 28 | 29 | * `obj` **{Object}** 30 | * `val` **{String|Array}** 31 | * `returns` **{Boolean}** 32 | 33 | **Example** 34 | 35 | ```js 36 | cu.has(['a', 'b', 'c'], 'c'); 37 | //=> true 38 | 39 | cu.has(['a', 'b', 'c'], ['c', 'z']); 40 | //=> true 41 | 42 | cu.has({a: 'b', c: 'd'}, ['c', 'z']); 43 | //=> true 44 | ``` 45 | 46 | ### [.hasAll](index.js#L90) 47 | 48 | Returns true if an array or object has all of the given values. 49 | 50 | **Params** 51 | 52 | * `val` **{Object|Array}** 53 | * `values` **{String|Array}** 54 | * `returns` **{Boolean}** 55 | 56 | **Example** 57 | 58 | ```js 59 | cu.hasAll(['a', 'b', 'c'], 'c'); 60 | //=> true 61 | 62 | cu.hasAll(['a', 'b', 'c'], ['c', 'z']); 63 | //=> false 64 | 65 | cu.hasAll({a: 'b', c: 'd'}, ['c', 'z']); 66 | //=> false 67 | ``` 68 | 69 | ### [.arrayify](index.js#L117) 70 | 71 | Cast the given value to an array. 72 | 73 | **Params** 74 | 75 | * `val` **{String|Array}** 76 | * `returns` **{Array}** 77 | 78 | **Example** 79 | 80 | ```js 81 | cu.arrayify('foo'); 82 | //=> ['foo'] 83 | 84 | cu.arrayify(['foo']); 85 | //=> ['foo'] 86 | ``` 87 | 88 | ### [.hasConstructor](index.js#L152) 89 | 90 | Returns true if a value has a `contructor` 91 | 92 | **Params** 93 | 94 | * `value` **{Object}** 95 | * `returns` **{Boolean}** 96 | 97 | **Example** 98 | 99 | ```js 100 | cu.hasConstructor({}); 101 | //=> true 102 | 103 | cu.hasConstructor(Object.create(null)); 104 | //=> false 105 | ``` 106 | 107 | ### [.nativeKeys](index.js#L174) 108 | 109 | Get the native `ownPropertyNames` from the constructor of the given `object`. An empty array is returned if the object does not have a constructor. 110 | 111 | **Params** 112 | 113 | * `obj` **{Object}**: Object that has a `constructor`. 114 | * `returns` **{Array}**: Array of keys. 115 | 116 | **Example** 117 | 118 | ```js 119 | cu.nativeKeys({a: 'b', b: 'c', c: 'd'}) 120 | //=> ['a', 'b', 'c'] 121 | 122 | cu.nativeKeys(function(){}) 123 | //=> ['length', 'caller'] 124 | ``` 125 | 126 | ### [.getDescriptor](index.js#L208) 127 | 128 | Returns property descriptor `key` if it's an "own" property of the given object. 129 | 130 | **Params** 131 | 132 | * `obj` **{Object}** 133 | * `key` **{String}** 134 | * `returns` **{Object}**: Returns descriptor `key` 135 | 136 | **Example** 137 | 138 | ```js 139 | function App() {} 140 | Object.defineProperty(App.prototype, 'count', { 141 | get: function() { 142 | return Object.keys(this).length; 143 | } 144 | }); 145 | cu.getDescriptor(App.prototype, 'count'); 146 | // returns: 147 | // { 148 | // get: [Function], 149 | // set: undefined, 150 | // enumerable: false, 151 | // configurable: false 152 | // } 153 | ``` 154 | 155 | ### [.copyDescriptor](index.js#L238) 156 | 157 | Copy a descriptor from one object to another. 158 | 159 | **Params** 160 | 161 | * `receiver` **{Object}** 162 | * `provider` **{Object}** 163 | * `name` **{String}** 164 | * `returns` **{Object}** 165 | 166 | **Example** 167 | 168 | ```js 169 | function App() {} 170 | Object.defineProperty(App.prototype, 'count', { 171 | get: function() { 172 | return Object.keys(this).length; 173 | } 174 | }); 175 | var obj = {}; 176 | cu.copyDescriptor(obj, App.prototype, 'count'); 177 | ``` 178 | 179 | ### [.copy](index.js#L264) 180 | 181 | Copy static properties, prototype properties, and descriptors 182 | from one object to another. 183 | 184 | **Params** 185 | 186 | * `receiver` **{Object}** 187 | * `provider` **{Object}** 188 | * `omit` **{String|Array}**: One or more properties to omit 189 | * `returns` **{Object}** 190 | 191 | ### [.inherit](index.js#L299) 192 | 193 | Inherit the static properties, prototype properties, and descriptors 194 | from of an object. 195 | 196 | **Params** 197 | 198 | * `receiver` **{Object}** 199 | * `provider` **{Object}** 200 | * `omit` **{String|Array}**: One or more properties to omit 201 | * `returns` **{Object}** 202 | 203 | ### [.extend](index.js#L343) 204 | 205 | Returns a function for extending the static properties, prototype properties, and descriptors from the `Parent` constructor onto `Child` constructors. 206 | 207 | **Params** 208 | 209 | * `Parent` **{Function}**: Parent ctor 210 | * `extend` **{Function}**: Optional extend function to handle custom extensions. Useful when updating methods that require a specific prototype. 211 | * `Child` **{Function}**: Child ctor 212 | * `proto` **{Object}**: Optionally pass additional prototype properties to inherit. 213 | * `returns` **{Object}** 214 | 215 | **Example** 216 | 217 | ```js 218 | var extend = cu.extend(Parent); 219 | Parent.extend(Child); 220 | 221 | // optional methods 222 | Parent.extend(Child, { 223 | foo: function() {}, 224 | bar: function() {} 225 | }); 226 | ``` 227 | 228 | ### [.bubble](index.js#L356) 229 | 230 | Bubble up events emitted from static methods on the Parent ctor. 231 | 232 | **Params** 233 | 234 | * `Parent` **{Object}** 235 | * `events` **{Array}**: Event names to bubble up 236 | 237 | ## About 238 | 239 |
240 | Contributing 241 | 242 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). 243 | 244 |
245 | 246 |
247 | Running Tests 248 | 249 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: 250 | 251 | ```sh 252 | $ npm install && npm test 253 | ``` 254 | 255 |
256 |
257 | Building docs 258 | 259 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ 260 | 261 | To generate the readme, run the following command: 262 | 263 | ```sh 264 | $ npm install -g verbose/verb#dev verb-generate-readme && verb 265 | ``` 266 | 267 |
268 | 269 | ### Related projects 270 | 271 | You might also be interested in these projects: 272 | 273 | * [define-property](https://www.npmjs.com/package/define-property): Define a non-enumerable property on an object. Uses Reflect.defineProperty when available, otherwise Object.defineProperty. | [homepage](https://github.com/jonschlinkert/define-property "Define a non-enumerable property on an object. Uses Reflect.defineProperty when available, otherwise Object.defineProperty.") 274 | * [delegate-properties](https://www.npmjs.com/package/delegate-properties): Deep-clone properties from one object to another and make them non-enumerable, or make existing properties… [more](https://github.com/jonschlinkert/delegate-properties) | [homepage](https://github.com/jonschlinkert/delegate-properties "Deep-clone properties from one object to another and make them non-enumerable, or make existing properties on an object non-enumerable.") 275 | * [is-descriptor](https://www.npmjs.com/package/is-descriptor): Returns true if a value has the characteristics of a valid JavaScript descriptor. Works for… [more](https://github.com/jonschlinkert/is-descriptor) | [homepage](https://github.com/jonschlinkert/is-descriptor "Returns true if a value has the characteristics of a valid JavaScript descriptor. Works for data descriptors and accessor descriptors.") 276 | 277 | ### Contributors 278 | 279 | | **Commits** | **Contributor** | 280 | | --- | --- | 281 | | 34 | [jonschlinkert](https://github.com/jonschlinkert) | 282 | | 8 | [doowb](https://github.com/doowb) | 283 | | 2 | [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) | 284 | 285 | ### Author 286 | 287 | **Jon Schlinkert** 288 | 289 | * [linkedin/in/jonschlinkert](https://linkedin.com/in/jonschlinkert) 290 | * [github/jonschlinkert](https://github.com/jonschlinkert) 291 | * [twitter/jonschlinkert](https://twitter.com/jonschlinkert) 292 | 293 | ### License 294 | 295 | Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert). 296 | Released under the [MIT License](LICENSE). 297 | 298 | *** 299 | 300 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on January 11, 2018._ -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var cu = require('./'); 2 | 3 | // function App() {} 4 | // Object.defineProperty(App.prototype, 'count', { 5 | // get: function () { 6 | // return Object.keys(this).length; 7 | // } 8 | // }); 9 | // console.log(cu.getDescriptor(App.prototype, 'count')); 10 | 11 | function defineProp (obj, name, fn) { 12 | Object.defineProperty(obj, name, { 13 | enumerable: true, 14 | configurable: true, 15 | get: function () { 16 | return fn(); 17 | } 18 | }); 19 | } 20 | 21 | function fn() { 22 | console.log('hey!'); 23 | return function (msg) { 24 | return 'foo ' + msg; 25 | }; 26 | } 27 | 28 | var one = { 29 | bar: function (msg) { 30 | return 'bar ' + msg; 31 | } 32 | }; 33 | var two = {}; 34 | defineProp(one, 'foo', fn); 35 | 36 | cu.copyDescriptor(two, one, 'foo'); 37 | cu.copyDescriptor(two, one, 'bar'); 38 | 39 | console.log(two.foo('a')) 40 | console.log(two.bar('b')) 41 | -------------------------------------------------------------------------------- /examples/a.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var App = require('./app'); 4 | var define = require('define-property'); 5 | 6 | function A(options) { 7 | App.call(this); 8 | this.options = options || {}; 9 | } 10 | 11 | define(A, 'metadata', { 12 | name: 'A', 13 | repository: 'foo/a' 14 | }); 15 | App.extend(A); 16 | 17 | A.prototype.set = function(key, value) { 18 | this[key] = value; 19 | return this; 20 | }; 21 | A.prototype.get = function(key) { 22 | return this[key]; 23 | }; 24 | A.prototype.del = function(key) { 25 | delete this[key]; 26 | }; 27 | Object.defineProperty(A.prototype, 'count', { 28 | get: function () { 29 | return Object.keys(this).length; 30 | }, 31 | set: function () { 32 | } 33 | }); 34 | module.exports = A; 35 | -------------------------------------------------------------------------------- /examples/app.js: -------------------------------------------------------------------------------- 1 | var define = require('define-property'); 2 | var cu = require('..'); 3 | 4 | function App(options) { 5 | this.options = options || {}; 6 | } 7 | App.prototype.set = function(key, value) { 8 | this[key] = value; 9 | return this; 10 | }; 11 | App.prototype.get = function(key) { 12 | return this[key]; 13 | }; 14 | App.prototype.del = function(key) { 15 | delete this[key]; 16 | }; 17 | Object.defineProperty(App.prototype, 'count', { 18 | get: function () { 19 | return Object.keys(this).length; 20 | }, 21 | set: function () { 22 | } 23 | }); 24 | 25 | define(App, 'metadata', { 26 | name: 'App', 27 | repository: 'app/app' 28 | }); 29 | 30 | App.extend = cu.extend(App); 31 | App.inherit = cu.inherit; 32 | 33 | module.exports = App; 34 | -------------------------------------------------------------------------------- /examples/b.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var A = require('./a'); 4 | var define = require('define-property'); 5 | 6 | function B(options) { 7 | A.call(this); 8 | this.options = options || {}; 9 | } 10 | 11 | define(B, 'metadata', { 12 | name: 'B', 13 | repository: 'foo/b' 14 | }); 15 | A.extend(B); 16 | 17 | B.prototype.set = function(key, value) { 18 | this[key] = value; 19 | return this; 20 | }; 21 | B.prototype.get = function(key) { 22 | return this[key]; 23 | }; 24 | B.prototype.del = function(key) { 25 | delete this[key]; 26 | }; 27 | Object.defineProperty(B.prototype, 'count', { 28 | get: function () { 29 | return Object.keys(this).length; 30 | }, 31 | set: function () { 32 | } 33 | }); 34 | 35 | define(B, 'metadata', { 36 | name: 'B', 37 | repository: 'foo/b' 38 | }); 39 | module.exports = B; 40 | -------------------------------------------------------------------------------- /examples/c.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var B = require('./b'); 4 | var define = require('define-property'); 5 | 6 | function C(options) { 7 | B.call(this); 8 | this.options = options || {}; 9 | } 10 | B.extend(C); 11 | define(C, 'metadata', { 12 | name: 'C', 13 | repository: 'foo/c' 14 | }); 15 | 16 | C.prototype.set = function(key, value) { 17 | this[key] = value; 18 | return this; 19 | }; 20 | C.prototype.get = function(key) { 21 | return this[key]; 22 | }; 23 | C.prototype.del = function(key) { 24 | delete this[key]; 25 | }; 26 | Object.defineProperty(C.prototype, 'count', { 27 | get: function () { 28 | return Object.keys(this).length; 29 | }, 30 | set: function () { 31 | } 32 | }); 33 | 34 | module.exports = C; 35 | -------------------------------------------------------------------------------- /examples/meta.js: -------------------------------------------------------------------------------- 1 | 2 | var B = require('./b'); 3 | var App = require('./c'); 4 | 5 | // console.log(App.metadata) 6 | var app = new App(); 7 | -------------------------------------------------------------------------------- /fixtures/app.js: -------------------------------------------------------------------------------- 1 | function App(options) { 2 | this.options = options || {}; 3 | } 4 | App.prototype.set = function(key, value) { 5 | this[key] = value; 6 | return this; 7 | }; 8 | App.prototype.get = function(key) { 9 | return this[key]; 10 | }; 11 | App.prototype.del = function(key) { 12 | delete this[key]; 13 | }; 14 | Object.defineProperty(App.prototype, 'count', { 15 | get: function () { 16 | return Object.keys(this).length; 17 | }, 18 | set: function () { 19 | } 20 | }); 21 | 22 | module.exports = App; -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var mocha = require('gulp-mocha'); 5 | var istanbul = require('gulp-istanbul'); 6 | var eslint = require('gulp-eslint'); 7 | var through = require('through2'); 8 | 9 | gulp.task('coverage', function() { 10 | return gulp.src(['index.js', 'utils.js']) 11 | .pipe(istanbul({includeUntested: true})) 12 | .pipe(istanbul.hookRequire()); 13 | }); 14 | 15 | gulp.task('mocha', ['coverage'], function() { 16 | return gulp.src('test.js') 17 | .pipe(mocha()) 18 | .pipe(istanbul.writeReports()); 19 | }); 20 | 21 | gulp.task('eslint', function() { 22 | return gulp.src('*.js') 23 | .pipe(eslint()) 24 | .pipe(eslint.format()); 25 | }); 26 | 27 | gulp.task('vars', function() { 28 | var utils = require('./utils'); 29 | var keys = Object.keys(utils); 30 | var report = {}; 31 | var cache = {}; 32 | 33 | return gulp.src(['index.js', 'utils.js']) 34 | .pipe(through.obj(function(file, enc, next) { 35 | var str = file.contents.toString(); 36 | keys.forEach(function(key) { 37 | report[key] = report[key] || 0; 38 | var re = cache[key] || (cache[key] = new RegExp('\\.' + key, 'g')); 39 | var m = str.match(re); 40 | if (!m) return; 41 | report[key]++; 42 | }); 43 | 44 | next(null, file); 45 | }, function(next) { 46 | var keys = Object.keys(report); 47 | var res = {}; 48 | 49 | keys.sort(function(a, b) { 50 | return report[a] > report[b] ? -1 : 1; 51 | }); 52 | 53 | keys.forEach(function(key) { 54 | res[key] = report[key]; 55 | }); 56 | 57 | console.log(res); 58 | console.log(keys.length, 'modules'); 59 | next(); 60 | })) 61 | }); 62 | 63 | gulp.task('default', ['mocha', 'eslint']); 64 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var union = require('arr-union'); 5 | var define = require('define-property'); 6 | var staticExtend = require('static-extend'); 7 | var isObj = require('isobject'); 8 | 9 | /** 10 | * Expose class utils 11 | */ 12 | 13 | var cu = module.exports; 14 | 15 | /** 16 | * Expose class utils: `cu` 17 | */ 18 | 19 | cu.isObject = function isObject(val) { 20 | return isObj(val) || typeof val === 'function'; 21 | }; 22 | 23 | /** 24 | * Returns true if an array has any of the given elements, or an 25 | * object has any of the give keys. 26 | * 27 | * ```js 28 | * cu.has(['a', 'b', 'c'], 'c'); 29 | * //=> true 30 | * 31 | * cu.has(['a', 'b', 'c'], ['c', 'z']); 32 | * //=> true 33 | * 34 | * cu.has({a: 'b', c: 'd'}, ['c', 'z']); 35 | * //=> true 36 | * ``` 37 | * @param {Object} `obj` 38 | * @param {String|Array} `val` 39 | * @return {Boolean} 40 | * @api public 41 | */ 42 | 43 | cu.has = function has(obj, val) { 44 | val = cu.arrayify(val); 45 | var len = val.length; 46 | 47 | if (cu.isObject(obj)) { 48 | for (var key in obj) { 49 | if (val.indexOf(key) > -1) { 50 | return true; 51 | } 52 | } 53 | 54 | var keys = cu.nativeKeys(obj); 55 | return cu.has(keys, val); 56 | } 57 | 58 | if (Array.isArray(obj)) { 59 | var arr = obj; 60 | while (len--) { 61 | if (arr.indexOf(val[len]) > -1) { 62 | return true; 63 | } 64 | } 65 | return false; 66 | } 67 | 68 | throw new TypeError('expected an array or object.'); 69 | }; 70 | 71 | /** 72 | * Returns true if an array or object has all of the given values. 73 | * 74 | * ```js 75 | * cu.hasAll(['a', 'b', 'c'], 'c'); 76 | * //=> true 77 | * 78 | * cu.hasAll(['a', 'b', 'c'], ['c', 'z']); 79 | * //=> false 80 | * 81 | * cu.hasAll({a: 'b', c: 'd'}, ['c', 'z']); 82 | * //=> false 83 | * ``` 84 | * @param {Object|Array} `val` 85 | * @param {String|Array} `values` 86 | * @return {Boolean} 87 | * @api public 88 | */ 89 | 90 | cu.hasAll = function hasAll(val, values) { 91 | values = cu.arrayify(values); 92 | var len = values.length; 93 | while (len--) { 94 | if (!cu.has(val, values[len])) { 95 | return false; 96 | } 97 | } 98 | return true; 99 | }; 100 | 101 | /** 102 | * Cast the given value to an array. 103 | * 104 | * ```js 105 | * cu.arrayify('foo'); 106 | * //=> ['foo'] 107 | * 108 | * cu.arrayify(['foo']); 109 | * //=> ['foo'] 110 | * ``` 111 | * 112 | * @param {String|Array} `val` 113 | * @return {Array} 114 | * @api public 115 | */ 116 | 117 | cu.arrayify = function arrayify(val) { 118 | return val ? (Array.isArray(val) ? val : [val]) : []; 119 | }; 120 | 121 | /** 122 | * Noop 123 | */ 124 | 125 | cu.noop = function noop() { 126 | return; 127 | }; 128 | 129 | /** 130 | * Returns the first argument passed to the function. 131 | */ 132 | 133 | cu.identity = function identity(val) { 134 | return val; 135 | }; 136 | 137 | /** 138 | * Returns true if a value has a `contructor` 139 | * 140 | * ```js 141 | * cu.hasConstructor({}); 142 | * //=> true 143 | * 144 | * cu.hasConstructor(Object.create(null)); 145 | * //=> false 146 | * ``` 147 | * @param {Object} `value` 148 | * @return {Boolean} 149 | * @api public 150 | */ 151 | 152 | cu.hasConstructor = function hasConstructor(val) { 153 | return cu.isObject(val) && typeof val.constructor !== 'undefined'; 154 | }; 155 | 156 | /** 157 | * Get the native `ownPropertyNames` from the constructor of the 158 | * given `object`. An empty array is returned if the object does 159 | * not have a constructor. 160 | * 161 | * ```js 162 | * cu.nativeKeys({a: 'b', b: 'c', c: 'd'}) 163 | * //=> ['a', 'b', 'c'] 164 | * 165 | * cu.nativeKeys(function(){}) 166 | * //=> ['length', 'caller'] 167 | * ``` 168 | * 169 | * @param {Object} `obj` Object that has a `constructor`. 170 | * @return {Array} Array of keys. 171 | * @api public 172 | */ 173 | 174 | cu.nativeKeys = function nativeKeys(val) { 175 | if (!cu.hasConstructor(val)) return []; 176 | var keys = Object.getOwnPropertyNames(val); 177 | if ('caller' in val) keys.push('caller'); 178 | return keys; 179 | }; 180 | 181 | /** 182 | * Returns property descriptor `key` if it's an "own" property 183 | * of the given object. 184 | * 185 | * ```js 186 | * function App() {} 187 | * Object.defineProperty(App.prototype, 'count', { 188 | * get: function() { 189 | * return Object.keys(this).length; 190 | * } 191 | * }); 192 | * cu.getDescriptor(App.prototype, 'count'); 193 | * // returns: 194 | * // { 195 | * // get: [Function], 196 | * // set: undefined, 197 | * // enumerable: false, 198 | * // configurable: false 199 | * // } 200 | * ``` 201 | * 202 | * @param {Object} `obj` 203 | * @param {String} `key` 204 | * @return {Object} Returns descriptor `key` 205 | * @api public 206 | */ 207 | 208 | cu.getDescriptor = function getDescriptor(obj, key) { 209 | if (!cu.isObject(obj)) { 210 | throw new TypeError('expected an object.'); 211 | } 212 | if (typeof key !== 'string') { 213 | throw new TypeError('expected key to be a string.'); 214 | } 215 | return Object.getOwnPropertyDescriptor(obj, key); 216 | }; 217 | 218 | /** 219 | * Copy a descriptor from one object to another. 220 | * 221 | * ```js 222 | * function App() {} 223 | * Object.defineProperty(App.prototype, 'count', { 224 | * get: function() { 225 | * return Object.keys(this).length; 226 | * } 227 | * }); 228 | * var obj = {}; 229 | * cu.copyDescriptor(obj, App.prototype, 'count'); 230 | * ``` 231 | * @param {Object} `receiver` 232 | * @param {Object} `provider` 233 | * @param {String} `name` 234 | * @return {Object} 235 | * @api public 236 | */ 237 | 238 | cu.copyDescriptor = function copyDescriptor(receiver, provider, name) { 239 | if (!cu.isObject(receiver)) { 240 | throw new TypeError('expected receiving object to be an object.'); 241 | } 242 | if (!cu.isObject(provider)) { 243 | throw new TypeError('expected providing object to be an object.'); 244 | } 245 | if (typeof name !== 'string') { 246 | throw new TypeError('expected name to be a string.'); 247 | } 248 | 249 | var val = cu.getDescriptor(provider, name); 250 | if (val) Object.defineProperty(receiver, name, val); 251 | }; 252 | 253 | /** 254 | * Copy static properties, prototype properties, and descriptors 255 | * from one object to another. 256 | * 257 | * @param {Object} `receiver` 258 | * @param {Object} `provider` 259 | * @param {String|Array} `omit` One or more properties to omit 260 | * @return {Object} 261 | * @api public 262 | */ 263 | 264 | cu.copy = function copy(receiver, provider, omit) { 265 | if (!cu.isObject(receiver)) { 266 | throw new TypeError('expected receiving object to be an object.'); 267 | } 268 | if (!cu.isObject(provider)) { 269 | throw new TypeError('expected providing object to be an object.'); 270 | } 271 | var props = Object.getOwnPropertyNames(provider); 272 | var keys = Object.keys(provider); 273 | var len = props.length, 274 | key; 275 | omit = cu.arrayify(omit); 276 | 277 | while (len--) { 278 | key = props[len]; 279 | 280 | if (cu.has(keys, key)) { 281 | define(receiver, key, provider[key]); 282 | } else if (!(key in receiver) && !cu.has(omit, key)) { 283 | cu.copyDescriptor(receiver, provider, key); 284 | } 285 | } 286 | }; 287 | 288 | /** 289 | * Inherit the static properties, prototype properties, and descriptors 290 | * from of an object. 291 | * 292 | * @param {Object} `receiver` 293 | * @param {Object} `provider` 294 | * @param {String|Array} `omit` One or more properties to omit 295 | * @api public 296 | */ 297 | 298 | cu.inherit = function inherit(receiver, provider, omit) { 299 | if (!cu.isObject(receiver)) { 300 | throw new TypeError('expected receiving object to be an object.'); 301 | } 302 | if (!cu.isObject(provider)) { 303 | throw new TypeError('expected providing object to be an object.'); 304 | } 305 | 306 | var keys = []; 307 | for (var key in provider) { 308 | keys.push(key); 309 | receiver[key] = provider[key]; 310 | } 311 | 312 | keys = keys.concat(cu.arrayify(omit)); 313 | 314 | var a = provider.prototype || provider; 315 | var b = receiver.prototype || receiver; 316 | cu.copy(b, a, keys); 317 | }; 318 | 319 | /** 320 | * Returns a function for extending the static properties, 321 | * prototype properties, and descriptors from the `Parent` 322 | * constructor onto `Child` constructors. 323 | * 324 | * ```js 325 | * var extend = cu.extend(Parent); 326 | * Parent.extend(Child); 327 | * 328 | * // optional methods 329 | * Parent.extend(Child, { 330 | * foo: function() {}, 331 | * bar: function() {} 332 | * }); 333 | * ``` 334 | * @param {Function} `Parent` Parent ctor 335 | * @param {Function} `extend` Optional extend function to handle custom extensions. Useful when updating methods that require a specific prototype. 336 | * @param {Function} `Child` Child ctor 337 | * @param {Object} `proto` Optionally pass additional prototype properties to inherit. 338 | * @return {Object} 339 | * @api public 340 | */ 341 | 342 | cu.extend = function() { 343 | // keep it lazy, instead of assigning to `cu.extend` 344 | return staticExtend.apply(null, arguments); 345 | }; 346 | 347 | /** 348 | * Bubble up events emitted from static methods on the Parent ctor. 349 | * 350 | * @param {Object} `Parent` 351 | * @param {Array} `events` Event names to bubble up 352 | * @api public 353 | */ 354 | 355 | cu.bubble = function(Parent, events) { 356 | events = events || []; 357 | Parent.bubble = function(Child, arr) { 358 | if (Array.isArray(arr)) { 359 | events = union([], events, arr); 360 | } 361 | var len = events.length; 362 | var idx = -1; 363 | while (++idx < len) { 364 | var name = events[idx]; 365 | Parent.on(name, Child.emit.bind(Child, name)); 366 | } 367 | cu.bubble(Child, events); 368 | }; 369 | }; 370 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "class-utils", 3 | "description": "Utils for working with JavaScript classes and prototype methods.", 4 | "version": "0.3.6", 5 | "homepage": "https://github.com/jonschlinkert/class-utils", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "contributors": [ 8 | "Brian Woodward (https://twitter.com/doowb)", 9 | "Jon Schlinkert (http://twitter.com/jonschlinkert)", 10 | "(https://github.com/wtgtybhertgeghgtwtg)" 11 | ], 12 | "repository": "jonschlinkert/class-utils", 13 | "bugs": { 14 | "url": "https://github.com/jonschlinkert/class-utils/issues" 15 | }, 16 | "license": "MIT", 17 | "files": [ 18 | "index.js" 19 | ], 20 | "main": "index.js", 21 | "engines": { 22 | "node": ">=0.10.0" 23 | }, 24 | "scripts": { 25 | "test": "mocha" 26 | }, 27 | "dependencies": { 28 | "arr-union": "^3.1.0", 29 | "define-property": "^0.2.5", 30 | "isobject": "^3.0.0", 31 | "static-extend": "^0.1.1" 32 | }, 33 | "devDependencies": { 34 | "gulp": "^3.9.1", 35 | "gulp-eslint": "^2.0.0", 36 | "gulp-format-md": "^0.1.7", 37 | "gulp-istanbul": "^0.10.3", 38 | "gulp-mocha": "^2.2.0", 39 | "mocha": "^2.4.5", 40 | "should": "^8.2.2", 41 | "through2": "^2.0.1" 42 | }, 43 | "keywords": [ 44 | "array", 45 | "assign", 46 | "class", 47 | "copy", 48 | "ctor", 49 | "define", 50 | "delegate", 51 | "descriptor", 52 | "extend", 53 | "extends", 54 | "inherit", 55 | "inheritance", 56 | "merge", 57 | "method", 58 | "object", 59 | "prop", 60 | "properties", 61 | "property", 62 | "prototype", 63 | "util", 64 | "utils" 65 | ], 66 | "verb": { 67 | "run": true, 68 | "toc": false, 69 | "layout": "default", 70 | "tasks": [ 71 | "readme" 72 | ], 73 | "plugins": [ 74 | "gulp-format-md" 75 | ], 76 | "related": { 77 | "list": [ 78 | "define-property", 79 | "delegate-properties", 80 | "is-descriptor" 81 | ] 82 | }, 83 | "reflinks": [ 84 | "verb" 85 | ], 86 | "lint": { 87 | "reflinks": true 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | require('mocha'); 3 | require('should'); 4 | var assert = require('assert'); 5 | var App = require('./fixtures/app'); 6 | var cu = require('./'); 7 | 8 | assert.hasAll = function(a, b) { 9 | assert(cu.hasAll(a, b)); 10 | }; 11 | assert.contains = function(a, b) { 12 | assert(cu.has(a, b)); 13 | }; 14 | 15 | describe('noop', function() { 16 | it('should return undefined:', function() { 17 | assert(typeof cu.noop({}) === 'undefined'); 18 | assert(typeof cu.noop('foo') === 'undefined'); 19 | }); 20 | }); 21 | 22 | describe('isObject', function() { 23 | it('should be true if the value is an object:', function() { 24 | assert(cu.isObject({})); 25 | }); 26 | it('should be true if the value is a function:', function() { 27 | assert(cu.isObject(function() {})); 28 | }); 29 | it('should be false if the value is an object:', function() { 30 | assert(!cu.isObject('foo')); 31 | }); 32 | }); 33 | 34 | describe('identity', function() { 35 | it('should return the given value:', function() { 36 | assert.deepEqual(cu.identity({}), {}); 37 | assert.deepEqual(cu.identity('foo'), 'foo'); 38 | assert.deepEqual(cu.identity(['foo']), ['foo']); 39 | assert.deepEqual(cu.identity([]), []); 40 | }); 41 | }); 42 | 43 | describe('hasAll', function() { 44 | describe('object', function() { 45 | it('should be true if an object has all given value:', function() { 46 | assert(cu.hasAll({ 47 | a: 'b', 48 | b: 'c', 49 | c: 'd' 50 | }, 'c')); 51 | assert(cu.hasAll({ 52 | a: 'b', 53 | b: 'c', 54 | c: 'd' 55 | }, ['c', 'b'])); 56 | assert(cu.hasAll({ 57 | a: 'b', 58 | b: 'c', 59 | c: 'd' 60 | }, ['a', 'b', 'c'])); 61 | }); 62 | 63 | it('should be false if an object does not have all given value:', function() { 64 | assert(!cu.hasAll({ 65 | a: 'b', 66 | b: 'c', 67 | c: 'd' 68 | }, 'd')); 69 | assert(!cu.hasAll({ 70 | a: 'b', 71 | b: 'c', 72 | c: 'd' 73 | }, ['c', 'b', 'z'])); 74 | }); 75 | }); 76 | 77 | describe('arrays', function() { 78 | it('should be true if an array has all given value:', function() { 79 | assert(cu.hasAll(['a', 'b', 'c'], 'c')); 80 | assert(cu.hasAll(['a', 'b', 'c'], ['c', 'b'])); 81 | assert(cu.hasAll(['a', 'b', 'c'], ['a', 'b', 'c'])); 82 | }); 83 | 84 | it('should be false if an array does not have all given value:', function() { 85 | assert(!cu.hasAll(['a', 'b', 'c'], 'd')); 86 | assert(!cu.hasAll(['a', 'b', 'c'], ['c', 'b', 'z'])); 87 | }); 88 | }); 89 | }); 90 | 91 | describe('has', function() { 92 | describe('objects', function() { 93 | it('should return true if an array has the given value:', function() { 94 | assert(cu.has({ 95 | a: 'b', 96 | b: 'c', 97 | c: 'd' 98 | }, 'c')); 99 | assert(cu.has({ 100 | a: 'b', 101 | b: 'c', 102 | c: 'd' 103 | }, 'b')); 104 | }); 105 | it('should return false if an array does not have the given value:', function() { 106 | assert(!cu.has({ 107 | a: 'b', 108 | b: 'c', 109 | c: 'd' 110 | }, 'd')); 111 | assert(!cu.has({ 112 | a: 'b', 113 | b: 'c', 114 | c: 'd' 115 | }, 'e')); 116 | }); 117 | it('should return true if an array has any given values:', function() { 118 | assert(cu.has({ 119 | a: 'b', 120 | b: 'c', 121 | c: 'd' 122 | }, ['c', 'z'])); 123 | assert(cu.has({ 124 | a: 'b', 125 | b: 'c', 126 | c: 'd' 127 | }, ['a', 'z'])); 128 | }); 129 | it('should return false if an array does not have any given values:', function() { 130 | assert(!cu.has({ 131 | a: 'b', 132 | b: 'c', 133 | c: 'd' 134 | }, ['x', 'z'])); 135 | assert(!cu.has({ 136 | a: 'b', 137 | b: 'c', 138 | c: 'd' 139 | }, ['y', 'z'])); 140 | }); 141 | }); 142 | 143 | describe('arrays', function() { 144 | it('should return true if an array has the given value:', function() { 145 | assert(cu.has(['a', 'b', 'c'], 'c')); 146 | assert(cu.has(['a', 'b', 'c'], 'b')); 147 | }); 148 | it('should return false if an array does not have the given value:', function() { 149 | assert(!cu.has(['a', 'b', 'c'], 'd')); 150 | assert(!cu.has(['a', 'b', 'c'], 'e')); 151 | }); 152 | it('should return true if an array has any given values:', function() { 153 | assert(cu.has(['a', 'b', 'c'], ['c', 'z'])); 154 | assert(cu.has(['a', 'b', 'c'], ['a', 'z'])); 155 | }); 156 | it('should return false if an array does not have any given values:', function() { 157 | assert(!cu.has(['a', 'b', 'c'], ['x', 'z'])); 158 | assert(!cu.has(['a', 'b', 'c'], ['y', 'z'])); 159 | }); 160 | }); 161 | 162 | it('should throw an error when the value is not an array or object:', function() { 163 | (function() { 164 | cu.has('foo'); 165 | }).should.throw('expected an array or object.'); 166 | }); 167 | }); 168 | 169 | describe('hasConstructor', function() { 170 | it('should return true if a value is an object and has a constructor:', function() { 171 | assert(cu.hasConstructor({ 172 | a: 'b', 173 | b: 'c', 174 | c: 'd' 175 | })); 176 | assert(cu.hasConstructor(function() {})); 177 | assert(cu.hasConstructor(App)); 178 | assert(cu.hasConstructor(new App())); 179 | assert(cu.hasConstructor(Object.create(new App()))); 180 | }); 181 | 182 | it('should return false if a value is not object:', function() { 183 | assert(!cu.hasConstructor('foo')); 184 | assert(!cu.hasConstructor(5)); 185 | }); 186 | 187 | it('should return false if an object does not have a constructor:', function() { 188 | assert(!cu.hasConstructor(Object.create(null))); 189 | assert(!cu.hasConstructor(null)); 190 | }); 191 | }); 192 | 193 | describe('nativeKeys', function() { 194 | it('should get the native keys of an object:', function() { 195 | assert.hasAll(cu.nativeKeys({ 196 | a: 'b', 197 | b: 'c', 198 | c: 'd' 199 | }), ['a', 'b', 'c']); 200 | assert.hasAll(cu.nativeKeys(function() {}), ['length', 'name', 'prototype']); 201 | assert.hasAll(cu.nativeKeys(App), ['length', 'name', 'prototype']); 202 | assert.hasAll(cu.nativeKeys(App.prototype), ['constructor', 'set', 'get', 'del']); 203 | assert.hasAll(cu.nativeKeys(App.constructor), ['length', 'name', 'caller']); 204 | assert.hasAll(cu.nativeKeys(App.prototype.constructor), ['length', 'caller']); 205 | assert.hasAll(cu.nativeKeys(new App()), ['options']); 206 | assert.hasAll(cu.nativeKeys(Object.create(new App())), []); 207 | }); 208 | 209 | it('should return empty array if a value does not have native keys:', function() { 210 | assert.deepEqual(cu.nativeKeys(Object.create(null)), []); 211 | assert.deepEqual(cu.nativeKeys(null), []); 212 | }); 213 | }); 214 | 215 | describe('getDescriptor', function() { 216 | it('should get the native keys of an object:', function() { 217 | assert.contains(cu.getDescriptor(App.prototype, 'count'), ['get', 218 | 'set' 219 | ]); 220 | assert(typeof cu.getDescriptor(App.prototype, 'foo') === 221 | 'undefined'); 222 | }); 223 | 224 | it('should throw an error when key is not a string:', function() { 225 | (function() { 226 | cu.getDescriptor({}, {}, null); 227 | }).should.throw('expected key to be a string.'); 228 | }); 229 | 230 | it('should throw an error when receiver is not an object:', function() { 231 | (function() { 232 | cu.getDescriptor('foo'); 233 | }).should.throw('expected an object.'); 234 | }); 235 | }); 236 | 237 | describe('copyDescriptor', function() { 238 | it('should copy a descriptor from the provider to receiver:', function() { 239 | var proto = App.prototype; 240 | var obj = {}; 241 | cu.copyDescriptor(obj, proto, 'count'); 242 | assert.contains(cu.getDescriptor(obj, 'count'), ['get', 'set']); 243 | }); 244 | 245 | it('should do nothing when the descriptor does not exist:', function() { 246 | var proto = App.prototype; 247 | var obj = {}; 248 | cu.copyDescriptor(obj, proto, 'foo'); 249 | assert.deepEqual(obj, {}); 250 | }); 251 | 252 | it('should throw an error when name is not a string:', function() { 253 | (function() { 254 | cu.copyDescriptor({}, {}, null); 255 | }).should.throw('expected name to be a string.'); 256 | }); 257 | 258 | it('should throw an error when receiver is not an object:', function() { 259 | (function() { 260 | cu.copyDescriptor('foo'); 261 | }).should.throw('expected receiving object to be an object.'); 262 | }); 263 | 264 | it('should throw an error when provider is not an object:', function() { 265 | (function() { 266 | cu.copyDescriptor({}, 'foo'); 267 | }).should.throw('expected providing object to be an object.'); 268 | }); 269 | }); 270 | 271 | describe('copy', function() { 272 | it('should copy descriptors from the provider to receiver:', function() { 273 | var proto = App.prototype; 274 | var obj = {}; 275 | cu.copy(obj, proto); 276 | assert.contains(cu.getDescriptor(obj, 'count'), ['get', 'set']); 277 | }); 278 | 279 | it('should copy properties from the provider to receiver:', function() { 280 | var proto = App.prototype; 281 | var obj = {}; 282 | cu.copy(obj, proto); 283 | obj.set('a', 'b'); 284 | assert(obj.a === 'b'); 285 | assert.contains(obj, ['get', 'set']); 286 | }); 287 | 288 | it('should do nothing when the property does not exist:', function() { 289 | var proto = App.prototype; 290 | var obj = {}; 291 | cu.copy(obj, proto, 'foo'); 292 | assert.deepEqual(obj, {}); 293 | }); 294 | 295 | it('should throw an error when receiver is not an object:', function() { 296 | (function() { 297 | cu.copy('foo'); 298 | }).should.throw('expected receiving object to be an object.'); 299 | }); 300 | 301 | it('should throw an error when provider is not an object:', function() { 302 | (function() { 303 | cu.copy({}, 'foo'); 304 | }).should.throw('expected providing object to be an object.'); 305 | }); 306 | }); 307 | 308 | describe('inherit', function() { 309 | it('should inherit descriptors from provider:', function() { 310 | var proto = App.prototype; 311 | var obj = {}; 312 | cu.inherit(obj, proto); 313 | assert.contains(cu.getDescriptor(obj, 'count'), ['get', 'set']); 314 | }); 315 | 316 | it('should inherit properties from provider:', function() { 317 | var proto = App.prototype; 318 | var obj = {}; 319 | cu.inherit(obj, proto); 320 | obj.set('a', 'b'); 321 | assert(obj.a === 'b'); 322 | assert.contains(obj, ['get', 'set', 'del']); 323 | }); 324 | 325 | it('should do nothing when the property does not exist:', function() { 326 | var proto = App.prototype; 327 | var obj = {}; 328 | cu.inherit(obj, proto, 'foo'); 329 | assert.deepEqual(obj, {}); 330 | }); 331 | 332 | it('should throw an error when receiver is not an object:', function() { 333 | (function() { 334 | cu.inherit('foo'); 335 | }).should.throw('expected receiving object to be an object.'); 336 | }); 337 | 338 | it('should throw an error when provider is not an object:', function() { 339 | (function() { 340 | cu.inherit({}, 'foo'); 341 | }).should.throw('expected providing object to be an object.'); 342 | }); 343 | }); 344 | 345 | describe('extend', function() { 346 | var Parent; 347 | var Ctor; 348 | var proto; 349 | 350 | beforeEach(function() { 351 | Parent = function() {} 352 | Parent.foo = 'bar'; 353 | Parent.prototype.a = function() {}; 354 | Parent.prototype.b = function() {}; 355 | Parent.prototype.c = function() {}; 356 | Object.defineProperty(Parent.prototype, 'count', { 357 | get: function() { 358 | return Object.keys(this).length; 359 | }, 360 | set: function() {} 361 | }); 362 | Ctor = function() { 363 | Parent.call(this); 364 | }; 365 | proto = App.prototype; 366 | }); 367 | 368 | it('should add `Parent.prototype` to `Ctor` instances as `_parent_`', function() { 369 | var extend = cu.extend(Parent); 370 | var instance1 = new Ctor(); 371 | assert.equal(typeof instance1._parent_, 'undefined'); 372 | extend(Ctor); 373 | var instance2 = new Ctor(); 374 | assert.equal(typeof instance2._parent_, 'object'); 375 | assert.deepEqual(instance2._parent_, Parent.prototype); 376 | }); 377 | 378 | it('should access `Parent` methods through `_parent_`', function() { 379 | Parent.prototype.upper = function(str) { 380 | return str.toUpperCase(); 381 | }; 382 | 383 | var extend = cu.extend(Parent); 384 | extend(Ctor); 385 | 386 | var instance = new Ctor(); 387 | assert.equal(instance.upper('foo'), 'FOO'); 388 | 389 | instance.upper = function(str) { 390 | return str; 391 | }; 392 | assert.equal(instance.upper('foo'), 'foo'); 393 | 394 | instance.upper = function(str) { 395 | return this._parent_.upper(str) + ' ' + str; 396 | }; 397 | assert.equal(instance.upper('foo'), 'FOO foo'); 398 | }); 399 | 400 | it('should add static methods to Ctor:', function() { 401 | var extend = cu.extend(Parent); 402 | extend(Ctor); 403 | assert(typeof Ctor.extend === 'function'); 404 | assert(Ctor.foo === 'bar'); 405 | }); 406 | 407 | it('should add prototype methods to Ctor:', function() { 408 | var extend = cu.extend(Parent); 409 | extend(Ctor); 410 | assert(typeof Ctor.prototype.a === 'function'); 411 | assert(typeof Ctor.prototype.b === 'function'); 412 | assert(typeof Ctor.prototype.c === 'function'); 413 | }); 414 | 415 | it('should add descriptors to Ctor:', function() { 416 | var extend = cu.extend(Parent); 417 | extend(Ctor); 418 | }); 419 | 420 | it('should copy prototype properties to Ctor:', function() { 421 | var extend = cu.extend(Parent); 422 | extend(Ctor, App.prototype); 423 | assert(typeof Ctor.prototype.get === 'function'); 424 | assert(typeof Ctor.prototype.set === 'function'); 425 | assert(typeof Ctor.prototype.del === 'function'); 426 | }); 427 | 428 | it('should add a mixin method to the prototype of Ctor using `extend` function:', function() { 429 | var extend = cu.extend(Parent, function(Child) { 430 | Child.prototype.mixin = function(key, val) { 431 | Child.prototype[key] = val; 432 | }; 433 | }); 434 | extend(Ctor, App.prototype); 435 | assert(typeof Ctor.prototype.mixin === 'function'); 436 | assert(typeof Ctor.prototype.get === 'function'); 437 | assert(typeof Ctor.prototype.set === 'function'); 438 | assert(typeof Ctor.prototype.del === 'function'); 439 | }); 440 | 441 | it('should mixin methods to the Ctor.prototype using `extend` function:', function() { 442 | var extend = cu.extend(Parent, function(Child) { 443 | Child.prototype.mixin = function(key, val) { 444 | Child.prototype[key] = val; 445 | }; 446 | }); 447 | extend(Ctor, App.prototype); 448 | var app = new Ctor(); 449 | app.mixin('foo', function() {}); 450 | assert.equal(typeof Ctor.prototype.foo, 'function'); 451 | }); 452 | 453 | it('should throw an error when Parent is not a function:', function() { 454 | (function() { 455 | cu.extend('foo'); 456 | }).should.throw('expected Parent to be a function.'); 457 | }); 458 | 459 | it('should throw an error when Ctor is not a function:', function() { 460 | (function() { 461 | cu.extend(function Foo() {})('bar') 462 | }).should.throw('expected Ctor to be a function.'); 463 | }); 464 | }); 465 | --------------------------------------------------------------------------------