├── .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 [](https://www.npmjs.com/package/class-utils) [](https://npmjs.org/package/class-utils) [](https://npmjs.org/package/class-utils) [](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 |
--------------------------------------------------------------------------------