Permalink
Browse files

Added new `extend` method to compose extenders

  • Loading branch information...
1 parent a20414a commit 53e3a2d4a3e4343a9f50d63fa1264893a82d413c @doug-martin committed Jan 13, 2013
Showing with 519 additions and 146 deletions.
  1. +74 −0 .jshintrc
  2. +102 −43 README.md
  3. +96 −32 docs/index.html
  4. +142 −59 extender.js
  5. +3 −0 extender.min.js
  6. +45 −11 grunt.js
  7. +1 −1 package.json
  8. +56 −0 test/extender.test.js
View
74 .jshintrc
@@ -0,0 +1,74 @@
+{
+ "predef": [
+ "jasmine",
+ "spyOn",
+ "it",
+ "console",
+ "describe",
+ "expect",
+ "beforeEach",
+ "afterEach",
+ "waits",
+ "waitsFor",
+ "runs",
+ "$",
+ "jQuery",
+ "_",
+ "require",
+ "define",
+ "sinon",
+ "thumbs"
+ ],
+
+ "node" : true,
+ "browser" : true,
+ "devel" : true,
+ "jquery" : true,
+
+ "bitwise" : false,
+ "camelcase" : true,
+ "curly" : true,
+ "eqeqeq" : true,
+ "forin" : false,
+ "immed" : true,
+ "indent" : 4,
+ "latedef" : true,
+ "newcap" : true,
+ "noarg" : true,
+ "noempty" : true,
+ "nonew" : false,
+ "plusplus" : false,
+ "quotmark" : false,
+ "regexp" : false,
+ "undef" : true,
+ "unused" : false,
+ "strict" : false,
+ "trailing" : true,
+ "white" : false,
+
+ "asi" : false,
+ "boss" : false,
+ "debug" : false,
+ "eqnull" : true,
+ "es5" : true,
+ "esnext" : true,
+ "evil" : false,
+ "expr" : true,
+ "funcscope" : false,
+ "globalstrict" : false,
+ "iterator" : false,
+ "lastsemic" : false,
+ "laxbreak" : false,
+ "laxcomma" : false,
+ "loopfunc" : false,
+ "multistr" : false,
+ "onecase" : false,
+ "proto" : false,
+ "regexdash" : false,
+ "scripturl" : false,
+ "smarttabs" : false,
+ "shadow" : false,
+ "sub" : true,
+ "supernew" : true,
+ "validthis" : false
+}
View
145 README.md
@@ -2,18 +2,18 @@
[![build status](https://secure.travis-ci.org/doug-martin/extender.png)](http://travis-ci.org/doug-martin/extender)
-#Extender
+# Extender
`extender` is a library that helps in making chainable APIs, by creating a function that accepts different values and returns an object decorated with functions based on the type.
-##Why Is Extender Different?
+## Why Is Extender Different?
Extender is different than normal chaining because is does more than return `this`. It decorates your values in a type safe manner.
For example if you return an array from a string based method then the returned value will be decorated with array methods and not the string methods. This allow you as the developer to focus on your API and not worrying about how to properly build and connect your API.
-##Installation
+## Installation
```
npm install extender
@@ -23,7 +23,7 @@ Or [download the source](https://raw.github.com/doug-martin/extender/master/exte
**Note** `extender` depends on [`declare.js`](http://doug-martin.github.com/declare.js/).
-###Requirejs
+### Requirejs
To use with requirejs place the `extend` source in the root scripts directory
@@ -35,7 +35,7 @@ define(["extender"], function(extender){
```
-##Usage
+## Usage
**`extender.define(tester, decorations)`**
@@ -50,18 +50,18 @@ function isString(obj) {
var myExtender = extender.define(isString, {
- multiply: function (str, times) {
- var ret = str;
- for (var i = 1; i < times; i++) {
- ret += str;
- }
- return ret;
- },
- toArray: function (str, delim) {
- delim = delim || "";
- return str.split(delim);
+ multiply: function (str, times) {
+ var ret = str;
+ for (var i = 1; i < times; i++) {
+ ret += str;
}
- });
+ return ret;
+ },
+ toArray: function (str, delim) {
+ delim = delim || "";
+ return str.split(delim);
+ }
+});
myExtender("hello").multiply(2).value(); //hellohello
@@ -77,7 +77,7 @@ function isUndefined(obj) {
}
function isUndefinedOrNull(obj) {
- var undef;
+var undef;
return obj === undef || obj === null;
}
@@ -95,11 +95,11 @@ function isString(obj) {
}
var myExtender = extender.define({
- isUndefined : isUndefined,
- isUndefinedOrNull : isUndefinedOrNull,
- isArray : isArray,
- isBoolean : isBoolean,
- isString : isString
+isUndefined : isUndefined,
+isUndefinedOrNull : isUndefinedOrNull,
+isArray : isArray,
+isBoolean : isBoolean,
+isString : isString
});
```
@@ -117,19 +117,19 @@ You can also chain extenders so that they accept multiple types and decorates ac
```javascript
myExtender
.define(isArray, {
- pluck: function (arr, m) {
- var ret = [];
- for (var i = 0, l = arr.length; i < l; i++) {
- ret.push(arr[i][m]);
- }
- return ret;
+ pluck: function (arr, m) {
+ var ret = [];
+ for (var i = 0, l = arr.length; i < l; i++) {
+ ret.push(arr[i][m]);
}
- })
+ return ret;
+ }
+})
.define(isBoolean, {
- invert: function (val) {
- return !val;
- }
- });
+ invert: function (val) {
+ return !val;
+ }
+});
myExtender([{a: "a"},{a: "b"},{a: "c"}]).pluck("a").value(); //["a", "b", "c"]
myExtender("I love javascript!").toArray(/\s+/).pluck("0"); //["I", "l", "j"]
@@ -157,10 +157,10 @@ When creating an extender you can also specify a constructor which will be invok
```javascript
myExtender.define(isString, {
- constructor : function(val){
- //set our value to the string trimmed
- this._value = val.trimRight().trimLeft();
- }
+constructor : function(val){
+ //set our value to the string trimmed
+ this._value = val.trimRight().trimLeft();
+}
});
```
@@ -188,26 +188,85 @@ myValidator().isNotNull().isEmailAddress().validator(); //now you dont need to c
```
+**`extender.extend(extendr)`**
-**Using `instanceof`**
+You may also compose extenders through the use of `extender.extend(extender)`, which will return an entirely new extender that is the composition of extenders.
-When using extenders you can test if a value is an `instanceof` of an extender by using the instanceof operator.
+Suppose you have the following two extenders.
```javascript
-var str = myExtender("hello");
+var myExtender = extender
+ .define({
+ isFunction: is.function,
+ isNumber: is.number,
+ isString: is.string,
+ isDate: is.date,
+ isArray: is.array,
+ isBoolean: is.boolean,
+ isUndefined: is.undefined,
+ isDefined: is.defined,
+ isUndefinedOrNull: is.undefinedOrNull,
+ isNull: is.null,
+ isArguments: is.arguments,
+ isInstanceOf: is.instanceOf,
+ isRegExp: is.regExp
+ });
+var myExtender2 = extender.define(is.array, {
+ pluck: function (arr, m) {
+ var ret = [];
+ for (var i = 0, l = arr.length; i < l; i++) {
+ ret.push(arr[i][m]);
+ }
+ return ret;
+ },
+
+ noWrap: {
+ pluckPlain: function (arr, m) {
+ var ret = [];
+ for (var i = 0, l = arr.length; i < l; i++) {
+ ret.push(arr[i][m]);
+ }
+ return ret;
+ }
+ }
+});
+
-str instanceof myExtender; //true
```
-##Examples
+And you do not want to alter either of them but instead what to create a third that is the union of the two.
-To see more examples click [here](https://github.com/doug-martin/extender/tree/master/examples)
+```javascript
+var composed = extender.extend(myExtender).extend(myExtender2);
+```
+So now you can use the new extender with the joined functionality if `myExtender` and `myExtender2`.
+
+```javascript
+var extended = composed([
+ {a: "a"},
+ {a: "b"},
+ {a: "c"}
+]);
+extended.isArray().value(); //true
+extended.pluck("a").value(); // ["a", "b", "c"]);
+
+```
+
+**Note** `myExtender` and `myExtender2` will **NOT** be altered.
+**Using `instanceof`**
+When using extenders you can test if a value is an `instanceof` of an extender by using the instanceof operator.
+```javascript
+var str = myExtender("hello");
+str instanceof myExtender; //true
+```
+## Examples
+To see more examples click [here](https://github.com/doug-martin/extender/tree/master/examples)
View
128 docs/index.html
@@ -209,18 +209,18 @@
var myExtender = extender.define(isString, {
- multiply: function (str, times) {
- var ret = str;
- for (var i = 1; i &lt; times; i++) {
- ret += str;
- }
- return ret;
- },
- toArray: function (str, delim) {
- delim = delim || &quot;&quot;;
- return str.split(delim);
+ multiply: function (str, times) {
+ var ret = str;
+ for (var i = 1; i &lt; times; i++) {
+ ret += str;
}
- });
+ return ret;
+ },
+ toArray: function (str, delim) {
+ delim = delim || &quot;&quot;;
+ return str.split(delim);
+ }
+});
myExtender(&quot;hello&quot;).multiply(2).value(); //hellohello</code></pre>
<p>If you do not specify a tester function and just pass in an object of <code>functions</code> then all values passed in will be decorated with methods.
@@ -233,7 +233,7 @@
}
function isUndefinedOrNull(obj) {
- var undef;
+var undef;
return obj === undef || obj === null;
}
@@ -251,11 +251,11 @@
}
var myExtender = extender.define({
- isUndefined : isUndefined,
- isUndefinedOrNull : isUndefinedOrNull,
- isArray : isArray,
- isBoolean : isBoolean,
- isString : isString
+isUndefined : isUndefined,
+isUndefinedOrNull : isUndefinedOrNull,
+isArray : isArray,
+isBoolean : isBoolean,
+isString : isString
});</code></pre>
<p>To use
@@ -268,19 +268,19 @@
</p>
<pre class='prettyprint linenums lang-js'><code class="lang-javascript">myExtender
.define(isArray, {
- pluck: function (arr, m) {
- var ret = [];
- for (var i = 0, l = arr.length; i &lt; l; i++) {
- ret.push(arr[i][m]);
- }
- return ret;
+ pluck: function (arr, m) {
+ var ret = [];
+ for (var i = 0, l = arr.length; i &lt; l; i++) {
+ ret.push(arr[i][m]);
}
- })
+ return ret;
+ }
+})
.define(isBoolean, {
- invert: function (val) {
- return !val;
- }
- });
+ invert: function (val) {
+ return !val;
+ }
+});
myExtender([{a: &quot;a&quot;},{a: &quot;b&quot;},{a: &quot;c&quot;}]).pluck(&quot;a&quot;).value(); //[&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
myExtender(&quot;I love javascript!&quot;).toArray(/\s+/).pluck(&quot;0&quot;); //[&quot;I&quot;, &quot;l&quot;, &quot;j&quot;]</code></pre>
@@ -312,10 +312,10 @@
</p>
<pre class='prettyprint linenums lang-js'><code class="lang-javascript">myExtender.define(isString, {
- constructor : function(val){
- //set our value to the string trimmed
- this._value = val.trimRight().trimLeft();
- }
+constructor : function(val){
+ //set our value to the string trimmed
+ this._value = val.trimRight().trimLeft();
+}
});</code></pre>
<p><strong><code>noWrap</code></strong>
@@ -340,6 +340,70 @@
});
myValidator().isNotNull().isEmailAddress().validator(); //now you dont need to call .value()</code></pre>
+<p><strong><code>extender.extend(extendr)</code></strong>
+
+</p>
+<p>You may also compose extenders through the use of <code>extender.extend(extender)</code>, which will return an entirely new extender that is the composition of extenders.
+
+</p>
+<p>Suppose you have the following two extenders.
+
+</p>
+<pre class='prettyprint linenums lang-js'><code class="lang-javascript">var myExtender = extender
+ .define({
+ isFunction: is.function,
+ isNumber: is.number,
+ isString: is.string,
+ isDate: is.date,
+ isArray: is.array,
+ isBoolean: is.boolean,
+ isUndefined: is.undefined,
+ isDefined: is.defined,
+ isUndefinedOrNull: is.undefinedOrNull,
+ isNull: is.null,
+ isArguments: is.arguments,
+ isInstanceOf: is.instanceOf,
+ isRegExp: is.regExp
+ });
+var myExtender2 = extender.define(is.array, {
+ pluck: function (arr, m) {
+ var ret = [];
+ for (var i = 0, l = arr.length; i &lt; l; i++) {
+ ret.push(arr[i][m]);
+ }
+ return ret;
+ },
+
+ noWrap: {
+ pluckPlain: function (arr, m) {
+ var ret = [];
+ for (var i = 0, l = arr.length; i &lt; l; i++) {
+ ret.push(arr[i][m]);
+ }
+ return ret;
+ }
+ }
+});</code></pre>
+<p>And you do not want to alter either of them but instead what to create a third that is the union of the two.
+
+
+</p>
+<pre class='prettyprint linenums lang-js'><code class="lang-javascript">var composed = extender.extend(myExtender).extend(myExtender2);</code></pre>
+<p>So now you can use the new extender with the joined functionality if <code>myExtender</code> and <code>myExtender2</code>.
+
+</p>
+<pre class='prettyprint linenums lang-js'><code class="lang-javascript">var extended = composed([
+ {a: &quot;a&quot;},
+ {a: &quot;b&quot;},
+ {a: &quot;c&quot;}
+]);
+extended.isArray().value(); //true
+extended.pluck(&quot;a&quot;).value(); // [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]);</code></pre>
+<p><strong>Note</strong> <code>myExtender</code> and <code>myExtender2</code> will <strong>NOT</strong> be altered.
+
+
+
+</p>
<p><strong>Using <code>instanceof</code></strong>
</p>
View
201 extender.js
@@ -1,4 +1,5 @@
(function () {
+ /*jshint strict:false*/
/**
@@ -7,18 +8,18 @@
* @github http://github.com/doug-martin/extender
* @header
* [![build status](https://secure.travis-ci.org/doug-martin/extender.png)](http://travis-ci.org/doug-martin/extender)
- * #Extender
+ * # Extender
*
* `extender` is a library that helps in making chainable APIs, by creating a function that accepts different values and returns an object decorated with functions based on the type.
*
- * ##Why Is Extender Different?
+ * ## Why Is Extender Different?
*
* Extender is different than normal chaining because is does more than return `this`. It decorates your values in a type safe manner.
*
* For example if you return an array from a string based method then the returned value will be decorated with array methods and not the string methods. This allow you as the developer to focus on your API and not worrying about how to properly build and connect your API.
*
*
- * ##Installation
+ * ## Installation
*
* ```
* npm install extender
@@ -28,7 +29,7 @@
*
* **Note** `extender` depends on [`declare.js`](http://doug-martin.github.com/declare.js/).
*
- * ###Requirejs
+ * ### Requirejs
*
* To use with requirejs place the `extend` source in the root scripts directory
*
@@ -40,7 +41,7 @@
* ```
*
*
- * ##Usage
+ * ## Usage
*
* **`extender.define(tester, decorations)`**
*
@@ -55,18 +56,18 @@
*
*
* var myExtender = extender.define(isString, {
- * multiply: function (str, times) {
- * var ret = str;
- * for (var i = 1; i < times; i++) {
- * ret += str;
- * }
- * return ret;
- * },
- * toArray: function (str, delim) {
- * delim = delim || "";
- * return str.split(delim);
- * }
- * });
+ * multiply: function (str, times) {
+ * var ret = str;
+ * for (var i = 1; i < times; i++) {
+ * ret += str;
+ * }
+ * return ret;
+ * },
+ * toArray: function (str, delim) {
+ * delim = delim || "";
+ * return str.split(delim);
+ * }
+ * });
*
* myExtender("hello").multiply(2).value(); //hellohello
*
@@ -82,7 +83,7 @@
* }
*
* function isUndefinedOrNull(obj) {
- * var undef;
+ * var undef;
* return obj === undef || obj === null;
* }
*
@@ -100,11 +101,11 @@
* }
*
* var myExtender = extender.define({
- * isUndefined : isUndefined,
- * isUndefinedOrNull : isUndefinedOrNull,
- * isArray : isArray,
- * isBoolean : isBoolean,
- * isString : isString
+ * isUndefined : isUndefined,
+ * isUndefinedOrNull : isUndefinedOrNull,
+ * isArray : isArray,
+ * isBoolean : isBoolean,
+ * isString : isString
* });
*
* ```
@@ -122,19 +123,19 @@
* ```javascript
* myExtender
* .define(isArray, {
- * pluck: function (arr, m) {
- * var ret = [];
- * for (var i = 0, l = arr.length; i < l; i++) {
- * ret.push(arr[i][m]);
- * }
- * return ret;
- * }
- * })
+ * pluck: function (arr, m) {
+ * var ret = [];
+ * for (var i = 0, l = arr.length; i < l; i++) {
+ * ret.push(arr[i][m]);
+ * }
+ * return ret;
+ * }
+ * })
* .define(isBoolean, {
- * invert: function (val) {
- * return !val;
- * }
- * });
+ * invert: function (val) {
+ * return !val;
+ * }
+ * });
*
* myExtender([{a: "a"},{a: "b"},{a: "c"}]).pluck("a").value(); //["a", "b", "c"]
* myExtender("I love javascript!").toArray(/\s+/).pluck("0"); //["I", "l", "j"]
@@ -162,10 +163,10 @@
*
* ```javascript
* myExtender.define(isString, {
- * constructor : function(val){
- * //set our value to the string trimmed
- * this._value = val.trimRight().trimLeft();
- * }
+ * constructor : function(val){
+ * //set our value to the string trimmed
+ * this._value = val.trimRight().trimLeft();
+ * }
* });
* ```
*
@@ -193,6 +194,74 @@
*
*
* ```
+ * **`extender.extend(extendr)`**
+ *
+ * You may also compose extenders through the use of `extender.extend(extender)`, which will return an entirely new extender that is the composition of extenders.
+ *
+ * Suppose you have the following two extenders.
+ *
+ * ```javascript
+ * var myExtender = extender
+ * .define({
+ * isFunction: is.function,
+ * isNumber: is.number,
+ * isString: is.string,
+ * isDate: is.date,
+ * isArray: is.array,
+ * isBoolean: is.boolean,
+ * isUndefined: is.undefined,
+ * isDefined: is.defined,
+ * isUndefinedOrNull: is.undefinedOrNull,
+ * isNull: is.null,
+ * isArguments: is.arguments,
+ * isInstanceOf: is.instanceOf,
+ * isRegExp: is.regExp
+ * });
+ * var myExtender2 = extender.define(is.array, {
+ * pluck: function (arr, m) {
+ * var ret = [];
+ * for (var i = 0, l = arr.length; i < l; i++) {
+ * ret.push(arr[i][m]);
+ * }
+ * return ret;
+ * },
+ *
+ * noWrap: {
+ * pluckPlain: function (arr, m) {
+ * var ret = [];
+ * for (var i = 0, l = arr.length; i < l; i++) {
+ * ret.push(arr[i][m]);
+ * }
+ * return ret;
+ * }
+ * }
+ * });
+ *
+ *
+ * ```
+ *
+ * And you do not want to alter either of them but instead what to create a third that is the union of the two.
+ *
+ *
+ * ```javascript
+ * var composed = extender.extend(myExtender).extend(myExtender2);
+ * ```
+ * So now you can use the new extender with the joined functionality if `myExtender` and `myExtender2`.
+ *
+ * ```javascript
+ * var extended = composed([
+ * {a: "a"},
+ * {a: "b"},
+ * {a: "c"}
+ * ]);
+ * extended.isArray().value(); //true
+ * extended.pluck("a").value(); // ["a", "b", "c"]);
+ *
+ * ```
+ *
+ * **Note** `myExtender` and `myExtender2` will **NOT** be altered.
+ *
+ *
*
* **Using `instanceof`**
*
@@ -204,7 +273,7 @@
* str instanceof myExtender; //true
* ```
*
- * ##Examples
+ * ## Examples
*
* To see more examples click [here](https://github.com/doug-martin/extender/tree/master/examples)
*/
@@ -226,18 +295,19 @@
return target;
}
- return function merge(obj, props) {
+ return function merge(obj) {
if (!obj) {
obj = {};
}
for (var i = 1, l = arguments.length; i < l; i++) {
_merge(obj, arguments[i]);
}
return obj; // Object
- }
+ };
}());
- function extender() {
+ function extender(supers) {
+ supers = supers || [];
var Base = declare({
instance: {
constructor: function (value) {
@@ -269,19 +339,18 @@
var extendedMethod;
if (name === "constructor") {
extendedMethod = function () {
- var args = slice.call(arguments);
this._super(arguments);
func.apply(this, arguments);
- }
+ };
} else {
extendedMethod = function extendedMethod() {
var args = slice.call(arguments);
- args.unshift(this._value)
+ args.unshift(this._value);
var ret = func.apply(this, args);
return ret !== undef ? _extender(ret) : this;
- }
+ };
}
- proto[name] = extendedMethod
+ proto[name] = extendedMethod;
}
function addNoWrapMethod(proto, name, func) {
@@ -291,18 +360,17 @@
var extendedMethod;
if (name === "constructor") {
extendedMethod = function () {
- var args = slice.call(arguments);
this._super(arguments);
func.apply(this, arguments);
- }
+ };
} else {
extendedMethod = function extendedMethod() {
var args = slice.call(arguments);
- args.unshift(this._value)
+ args.unshift(this._value);
return func.apply(this, args);
- }
+ };
}
- proto[name] = extendedMethod
+ proto[name] = extendedMethod;
}
function decorateProto(proto, decoration, nowrap) {
@@ -312,7 +380,7 @@
if (i === "noWrap") {
decorateProto(proto, decoration[i], true);
} else if (nowrap) {
- addNoWrapMethod(proto, i, decoration[i])
+ addNoWrapMethod(proto, i, decoration[i]);
} else {
addMethod(proto, i, decoration[i]);
}
@@ -324,10 +392,10 @@
}
function _extender(obj) {
- var ret = obj;
+ var ret = obj, i, l;
if (!(obj instanceof Base)) {
var base = {}, instance = (base.instance = {});
- for (var i = 0, l = defined.length; i < l; i++) {
+ for (i = 0, l = defined.length; i < l; i++) {
var definer = defined[i];
if (definer[0](obj)) {
merge(instance, definer[1]);
@@ -342,27 +410,42 @@
return true;
}
- function define(tester, decorate, chain) {
+ function define(tester, decorate) {
if (!decorate) {
decorate = tester;
tester = always;
}
+ decorate = decorate || {};
var proto = {};
decorateProto(proto, decorate);
defined.push([tester, proto]);
return _extender;
}
+ function extend(supr) {
+ if (supr && supr.hasOwnProperty("__defined__")) {
+ defined = defined.concat(supr["__defined__"]);
+ }
+ return _extender;
+ }
+
_extender.define = define;
+ _extender.extend = extend;
+ _extender["__defined__"] = defined;
+
return _extender;
}
return {
define: function () {
return extender().define.apply(extender, arguments);
+ },
+
+ extend: function (supr) {
+ return extender().define().extend(supr);
}
- }
+ };
}
@@ -376,7 +459,7 @@
return defineExtender((require("declare.js")));
});
} else {
- this.extender = defineExtender(declare);
+ this.extender = defineExtender(this.declare);
}
}).call(this);
View
3 extender.min.js
@@ -0,0 +1,3 @@
+/*! extender - v0.0.2 - 2013-01-12
+* Copyright (c) 2013 Doug Martin (blog.dougamartin.com); Licensed MIT */
+(function(){function e(e){function i(i){function u(e,r,i){if("function"!=typeof i)throw new TypeError("when extending type you must provide a function");var s;r==="constructor"?s=function(){this._super(arguments),i.apply(this,arguments)}:s=function(){var r=t.call(arguments);r.unshift(this._value);var s=i.apply(this,r);return s!==n?l(s):this},e[r]=s}function a(e,n,r){if("function"!=typeof r)throw new TypeError("when extending type you must provide a function");var i;n==="constructor"?i=function(){this._super(arguments),r.apply(this,arguments)}:i=function(){var n=t.call(arguments);return n.unshift(this._value),r.apply(this,n)},e[n]=i}function f(e,t,n){for(var r in t)t.hasOwnProperty(r)&&(r!=="getters"&&r!=="setters"?r==="noWrap"?f(e,t[r],!0):n?a(e,r,t[r]):u(e,r,t[r]):e[r]=t[r])}function l(e){var t=e,n,i;if(!(e instanceof s)){var u={},a=u.instance={};for(n=0,i=o.length;n<i;n++){var f=o[n];f[0](e)&&r(a,f[1])}t=new(s.extend(u))(e)}return t}function c(){return!0}function h(e,t){t||(t=e,e=c),t=t||{};var n={};return f(n,t),o.push([e,n]),l}function p(e){return e&&e.hasOwnProperty("__defined__")&&(o=o.concat(e.__defined__)),l}i=i||[];var s=e({instance:{constructor:function(e){this._value=e},value:function(){return this._value},eq:function(t){return l(this._value===t)},neq:function(t){return l(this._value!==t)},print:function(){return console.log(this._value),this}}}),o=[];return l.define=h,l.extend=p,l.__defined__=o,l}var t=Array.prototype.slice,n,r=function(){function t(e,t){var n,r;for(n in t)if(t.hasOwnProperty(n)){r=t[n];if(!(n in e)||e[n]!==r)e[n]=r}return e}return function(n){n||(n={});for(var r=1,i=arguments.length;r<i;r++)t(n,arguments[r]);return n}}();return{define:function(){return i().define.apply(i,arguments)},extend:function(e){return i().define().extend(e)}}}"undefined"!=typeof exports?"undefined"!=typeof module&&module.exports&&(module.exports=e(require("declare.js"))):"function"==typeof define?define(["require"],function(t){return e(t("declare.js"))}):this.extender=e(this.declare)}).call(this);
View
56 grunt.js
@@ -1,22 +1,56 @@
+/*global module:false*/
module.exports = function (grunt) {
+ var fs = require('fs');
+
+ // grunt doesn't natively support reading config from .jshintrc yet
+ var jshintOptions = JSON.parse(fs.readFileSync('./.jshintrc'));
+
+ // Project configuration.
grunt.initConfig({
pkg: '<json:package.json>',
meta: {
- banner:'// <%= pkg.title || pkg.name %> - v<%= pkg.version %> \n\n' +
- '// Built <%= grunt.template.today("yyyy-mm-dd") %>\n' +
- '// Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;\n' +
- '// Licensed <%= pkg.license %>'
+ banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
+ '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
+ '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
+ '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;' +
+ ' Licensed <%= pkg.license %> */'
+ },
+
+ jshint: {
+ options: jshintOptions,
+ globals: jshintOptions.predef
+ },
+
+ lint: {
+ files: [
+ 'extender.js'
+ ]
+ },
+
+ it: {
+ all: {
+ src: 'test/**/*.test.js',
+ options: {
+ timeout: 3000, // not fully supported yet
+ reporter: 'dotmatrix'
+ }
+ }
},
min: {
dist: {
- src: ['<banner>','extender.js'],
- dest: 'extender-min.js'
+ src: ['<banner:meta.banner>', 'extender.js'],
+ dest: '<%= pkg.name %>.min.js'
}
},
- uglify: {
- mangle: {toplevel: true},
- squeeze: {dead_code: false},
- codegen: {quote_keys: true}
- }
+ watch: {
+ files: '<config:lint.files>',
+ tasks: 'lint it'
+ },
+ uglify: {}
});
+
+ // Default task.
+ grunt.registerTask('default', 'lint it min');
+ grunt.loadNpmTasks('grunt-it');
+
};
View
2 package.json
@@ -1,6 +1,6 @@
{
"name": "extender",
- "version": "0.0.2",
+ "version": "0.0.3",
"description": "Easily create object decorators!",
"main": "index.js",
"scripts": {
View
56 test/extender.test.js
@@ -152,5 +152,61 @@ it.describe("extender",function (it) {
assert.isFalse(myExtender(extended.pluck).isFunction().value());
});
+ it.should("allow extending extenders", function () {
+ var myExtender = extender
+ .define({
+ isFunction: is.function,
+ isNumber: is.number,
+ isString: is.string,
+ isDate: is.date,
+ isArray: is.array,
+ isBoolean: is.boolean,
+ isUndefined: is.undefined,
+ isDefined: is.defined,
+ isUndefinedOrNull: is.undefinedOrNull,
+ isNull: is.null,
+ isArguments: is.arguments,
+ isInstanceOf: is.instanceOf,
+ isRegExp: is.regExp
+ });
+ var myExtender2 = extender.define(is.array, {
+ pluck: function (arr, m) {
+ var ret = [];
+ for (var i = 0, l = arr.length; i < l; i++) {
+ ret.push(arr[i][m]);
+ }
+ return ret;
+ },
+
+ noWrap: {
+ pluckPlain: function (arr, m) {
+ var ret = [];
+ for (var i = 0, l = arr.length; i < l; i++) {
+ ret.push(arr[i][m]);
+ }
+ return ret;
+ }
+ }
+ });
+ var composed = extender.extend(myExtender).extend(myExtender2);
+
+ var extended = composed([
+ {a: "a"},
+ {a: "b"},
+ {a: "c"}
+ ]);
+ assert.isTrue(extended.isArray().value());
+ assert.deepEqual(extended.pluck("a").value(), ["a", "b", "c"]);
+
+ //it should not alter the compsing extenders
+ assert.throws(function () {
+ myExtender("hello").pluck();
+ });
+ assert.throws(function () {
+ myExtender2("hello").isString();
+ });
+
+ });
+
}).run();

0 comments on commit 53e3a2d

Please sign in to comment.