Permalink
Browse files

Added global include method and made Mixable.extend & .include essent…

…ially just shortcuts to the global methods
  • Loading branch information...
1 parent 0744152 commit f14741ab51cfe9f054189b9ad5eb54e82074b3e3 @petebrowne committed Jul 26, 2011
Showing with 224 additions and 72 deletions.
  1. +55 −30 README.md
  2. +37 −23 dist/mixable.js
  3. +1 −1 dist/mixable.min.js
  4. +106 −2 spec/mixable-spec.coffee
  5. +25 −16 src/mixable.coffee
View
85 README.md
@@ -25,38 +25,30 @@ new Foo().instanceMethod() # => "an instance method!"
_Note: There is no support for `super` or method lookups. Mixable essentially just copies over properties from one object to another. If you need support for method lookups, look at [Classify](http://classify.petebrowne.com)._
-`extend(object, mixin)`
------------------------
+extend
+------
-Adds each property from the mixin to the given object. This is a global method, provided for convience.
+`extend(object, mixins...)`
-```coffee-script
-Mixin =
- method: -> "a mixin method!"
-
-object = {}
-extend object, Mixin
-object.method() # => "a mixin method!"
-```
-
-It could be used when you cannot extend the Mixable class:
+Adds the properties of each given mixin to the given object.
```coffee-script
-class Foo extends SomethingElse
- extend @, Mixin
+Mixin =
+ method: -> "a method!"
+
+foo = {}
+extend foo, Mixin
+foo.method() # => "a method!"
```
-`Mixable.extend(mixins...)`
----------------------------
-
-Adds the properties of each given mixin as class properties.
+This can be used within a class body when you cannot extend the Mixable class.
```coffee-script
Mixin =
method: -> "a class method!"
-class Foo extends Mixable
- @extend Mixin
+class Foo
+ extend @, Mixin
Foo.method() # => "a class method!"
```
@@ -68,25 +60,42 @@ class Mixin
@classMethod: -> "a class method!"
instanceMethod: -> "an instance method!"
-class Foo extends Mixable
- @extend Mixin
+class Foo
+ extend @, Mixin
Foo.classMethod() # => TypeError: Foo has no method 'classMethod'
Foo.instanceMethod() # => "an instance method!"
```
+---
-`Mixable.include(mixins...)`
-----------------------------
+`Mixable.extend(mixins...)`
-Adds the properties of each given mixin as instance properties.
+Adds the properties of each given mixin as class properties. Essentially a shortcut for `extend @, mixins...`.
```coffee-script
Mixin =
- method: -> "an instance method!"
+ method: -> "a class method!"
class Foo extends Mixable
- @include Mixin
+ @extend Mixin
+
+Foo.method() # => "a class method!"
+```
+
+include
+-------
+
+`include(object, mixins...)`
+
+Adds the properties of each given mixin as instance properties. This assumes the `object` is actually a class, and will throw a `TypeError` if it does not have a protoype.
+
+```coffee-script
+Mixin =
+ method: -> "an instance method!"
+
+class Foo
+ include @, Mixin
new Foo().method() # => "an instance method!"
```
@@ -98,13 +107,29 @@ class Mixin
@classMethod: -> "a class method!"
instanceMethod: -> "an instance method!"
-class Foo extends Mixable
- @include Mixin
+class Foo
+ include @, Mixin
Foo.classMethod() # => "a class method!"
new Foo().instanceMethod() # => "an instance method!"
```
---
+`Mixable.include(mixins...)`
+
+Adds the properties of each given mixin as instance properties. Essentially a shortcut for `include @, mixins...`.
+
+```coffee-script
+Mixin =
+ method: -> "an instance method!"
+
+class Foo extends Mixable
+ @include Mixin
+
+new Foo().method() # => "an instance method!"
+```
+
+---
+
Copyright (c) 2011 [Pete Browne](http://petebrowne.com). See LICENSE for details.
View
60 dist/mixable.js
@@ -6,15 +6,15 @@
* See LICENSE for details
*/
(function() {
- var Mixable, SKIP_PROPERTIES, extend, root;
+ var Mixable, SKIP_PROPERTIES, extend, include, root, __extend;
var __hasProp = Object.prototype.hasOwnProperty, __indexOf = Array.prototype.indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (this[i] === item) return i;
}
return -1;
}, __slice = Array.prototype.slice;
SKIP_PROPERTIES = ["constructor", "extended", "included", "prototype"];
- extend = function(object, mixin) {
+ __extend = function(object, mixin) {
var method, name, _results;
_results = [];
for (name in mixin) {
@@ -26,38 +26,52 @@
}
return _results;
};
+ extend = function() {
+ var mixin, mixins, object, _i, _len, _ref, _results;
+ object = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ _results = [];
+ for (_i = 0, _len = mixins.length; _i < _len; _i++) {
+ mixin = mixins[_i];
+ __extend(object, (_ref = mixin.prototype) != null ? _ref : mixin);
+ _results.push(mixin.extended != null ? mixin.extended(object) : void 0);
+ }
+ return _results;
+ };
+ include = function() {
+ var mixin, mixins, object, _i, _len, _results;
+ object = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ if (object.prototype == null) {
+ throw new TypeError;
+ }
+ _results = [];
+ for (_i = 0, _len = mixins.length; _i < _len; _i++) {
+ mixin = mixins[_i];
+ if (mixin.prototype != null) {
+ __extend(object, mixin);
+ __extend(object.prototype, mixin.prototype);
+ } else {
+ __extend(object.prototype, mixin);
+ }
+ _results.push(mixin.included != null ? mixin.included(object) : void 0);
+ }
+ return _results;
+ };
Mixable = (function() {
function Mixable() {}
Mixable.extend = function() {
- var mixin, mixins, _i, _len, _ref, _results;
+ var mixins;
mixins = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- _results = [];
- for (_i = 0, _len = mixins.length; _i < _len; _i++) {
- mixin = mixins[_i];
- extend(this, (_ref = mixin.prototype) != null ? _ref : mixin);
- _results.push(mixin.extended != null ? mixin.extended(this) : void 0);
- }
- return _results;
+ return extend.apply(null, [this].concat(__slice.call(mixins)));
};
Mixable.include = function() {
- var mixin, mixins, _i, _len, _results;
+ var mixins;
mixins = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- _results = [];
- for (_i = 0, _len = mixins.length; _i < _len; _i++) {
- mixin = mixins[_i];
- if (mixin.prototype != null) {
- extend(this, mixin);
- extend(this.prototype, mixin.prototype);
- } else {
- extend(this.prototype, mixin);
- }
- _results.push(mixin.included != null ? mixin.included(this) : void 0);
- }
- return _results;
+ return include.apply(null, [this].concat(__slice.call(mixins)));
};
return Mixable;
})();
root = typeof exports !== "undefined" && exports !== null ? exports : window;
root.extend = extend;
+ root.include = include;
root.Mixable = Mixable;
}).call(this);
View
2 dist/mixable.min.js
@@ -5,4 +5,4 @@
* Copyright (c) 2011, Pete Browne
* See LICENSE for details
*/
-(function(){var a,b,c,d,e=Object.prototype.hasOwnProperty,f=Array.prototype.indexOf||function(a){for(var b=0,c=this.length;b<c;b++)if(this[b]===a)return b;return-1},g=Array.prototype.slice;b=["constructor","extended","included","prototype"],c=function(a,c){var d,g,h;h=[];for(g in c){if(!e.call(c,g))continue;d=c[g],f.call(b,g)<0&&h.push(a[g]=d)}return h},a=function(){function a(){}a.extend=function(){var a,b,d,e,f,h;b=1<=arguments.length?g.call(arguments,0):[],h=[];for(d=0,e=b.length;d<e;d++)a=b[d],c(this,(f=a.prototype)!=null?f:a),h.push(a.extended!=null?a.extended(this):void 0);return h},a.include=function(){var a,b,d,e,f;b=1<=arguments.length?g.call(arguments,0):[],f=[];for(d=0,e=b.length;d<e;d++)a=b[d],a.prototype!=null?(c(this,a),c(this.prototype,a.prototype)):c(this.prototype,a),f.push(a.included!=null?a.included(this):void 0);return f};return a}(),d=typeof exports!="undefined"&&exports!==null?exports:window,d.extend=c,d.Mixable=a}).call(this)
+(function(){var a,b,c,d,e,f,g=Object.prototype.hasOwnProperty,h=Array.prototype.indexOf||function(a){for(var b=0,c=this.length;b<c;b++)if(this[b]===a)return b;return-1},i=Array.prototype.slice;b=["constructor","extended","included","prototype"],f=function(a,c){var d,e,f;f=[];for(e in c){if(!g.call(c,e))continue;d=c[e],h.call(b,e)<0&&f.push(a[e]=d)}return f},c=function(){var a,b,c,d,e,g,h;c=arguments[0],b=2<=arguments.length?i.call(arguments,1):[],h=[];for(d=0,e=b.length;d<e;d++)a=b[d],f(c,(g=a.prototype)!=null?g:a),h.push(a.extended!=null?a.extended(c):void 0);return h},d=function(){var a,b,c,d,e,g;c=arguments[0],b=2<=arguments.length?i.call(arguments,1):[];if(c.prototype==null)throw new TypeError;g=[];for(d=0,e=b.length;d<e;d++)a=b[d],a.prototype!=null?(f(c,a),f(c.prototype,a.prototype)):f(c.prototype,a),g.push(a.included!=null?a.included(c):void 0);return g},a=function(){function a(){}a.extend=function(){var a;a=1<=arguments.length?i.call(arguments,0):[];return c.apply(null,[this].concat(i.call(a)))},a.include=function(){var a;a=1<=arguments.length?i.call(arguments,0):[];return d.apply(null,[this].concat(i.call(a)))};return a}(),e=typeof exports!="undefined"&&exports!==null?exports:window,e.extend=c,e.include=d,e.Mixable=a}).call(this)
View
108 spec/mixable-spec.coffee
@@ -1,16 +1,120 @@
describe "extend", ->
- it "copies the given properties onto the given object", ->
+ it "adds an object's methods", ->
Foo =
methodA: -> true
methodB: -> false
bar = {}
extend bar, Foo
+
expect(bar.methodA()).toBe true
expect(bar.methodB()).toBe false
+
+ it "adds an object's methods as class methods", ->
+ Foo =
+ methodA: -> true
+ methodB: -> false
+
+ class Bar
+ extend @, Foo
+
+ expect(Bar.methodA()).toBe true
+ expect(Bar.methodB()).toBe false
+
+ it "adds a mixin's instance methods as class methods", ->
+ class Foo
+ methodA: -> true
+ methodB: -> false
+
+ class Bar
+ extend @, Foo
+
+ expect(Bar.methodA()).toBe true
+ expect(Bar.methodB()).toBe false
+
+ it "calls the mixin's .extended method", ->
+ class Foo
+ @extended: (base) ->
+ @extendedBy = base
+
+ class Bar
+ extend @, Foo
+
+ expect(Foo.extendedBy).toBe Bar
+
+describe "include", ->
+ it "adds an object's methods", ->
+ Foo =
+ methodA: -> true
+ methodB: -> false
+
+ bar = {}
+ expect(->
+ include bar, Foo
+ ).toThrow # TypeError
+
+ it "adds an object's methods as class methods", ->
+ Foo =
+ methodA: -> true
+ methodB: -> false
+
+ class Bar
+ include @, Foo
+
+ expect(new Bar().methodA()).toBe true
+ expect(new Bar().methodB()).toBe false
+
+ it "adds a mixin's instance methods as instance methods", ->
+ class Foo
+ methodA: -> true
+ methodB: -> false
+
+ class Bar
+ include @, Foo
+
+ expect(new Bar().methodA()).toBe true
+ expect(new Bar().methodB()).toBe false
+
+ it "adds a mixin's class methods as class methods", ->
+ class Foo
+ @methodA: -> true
+ @methodB: -> false
+
+ class Bar
+ include @, Foo
+
+ expect(Bar.methodA()).toBe true
+ expect(Bar.methodB()).toBe false
+
+ it "calls the mixin's .included method", ->
+ class Foo
+ @included: (base) ->
+ @includedBy = base
+
+ class Bar
+ include @, Foo
+
+ expect(Foo.includedBy).toBe Bar
+
+ it "does not add the mixin's .included method", ->
+ class Foo
+ @included: ->
+
+ class Bar
+ include @, Foo
+
+ expect(Bar.included).toBeUndefined()
+
+ it "does not add the mixin's .extended method", ->
+ class Foo
+ @extended: ->
+
+ class Bar
+ include @, Foo
+
+ expect(Bar.extended).toBeUndefined()
describe "Mixable", ->
-
describe ".extend", ->
it "adds an object's methods as class methods", ->
Foo =
View
41 src/mixable.coffee
@@ -7,35 +7,44 @@ SKIP_PROPERTIES = [
]
# Adds each property from the mixin to the given object.
-extend = (object, mixin) ->
+__extend = (object, mixin) ->
for own name, method of mixin when name not in SKIP_PROPERTIES
object[name] = method
+
+# Adds the instance methods from each given mixin
+# as class methods.
+extend = (object, mixins...) ->
+ for mixin in mixins
+ __extend object, mixin:: ? mixin
+ mixin.extended object if mixin.extended?
+
+# Adds the instance and class methods from each
+# given mixin.
+include = (object, mixins...) ->
+ throw new TypeError unless object::?
+ for mixin in mixins
+ if mixin::?
+ __extend object, mixin
+ __extend object::, mixin::
+ else
+ __extend object::, mixin
+ mixin.included object if mixin.included?
# Base class for adding mixins, Ruby style, to CoffeeScript
# classes. There is no support for super. This essentially
# just copies over properties from objects and classes.
class Mixable
-
- # Adds the instance methods from each given mixin
- # as class methods.
+ # Calls extend method, with `this` as the object.
@extend: (mixins...) ->
- for mixin in mixins
- extend @, mixin:: ? mixin
- mixin.extended @ if mixin.extended?
+ extend @, mixins...
- # Adds the instance and class methods from each
- # mixin given.
+ # Calls include method, with `this` as the object.
@include: (mixins...) ->
- for mixin in mixins
- if mixin::?
- extend @, mixin
- extend @::, mixin::
- else
- extend @::, mixin
- mixin.included @ if mixin.included?
+ include @, mixins...
# Expose the public API
root = exports ? window
root.extend = extend
+root.include = include
root.Mixable = Mixable

0 comments on commit f14741a

Please sign in to comment.