Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial commit, MooTools Class 1.x compat for Prime

  • Loading branch information...
commit 375ebe907987ccc124d9a5c30be46dce0ae23e60 0 parents
@arian authored
Showing with 492 additions and 0 deletions.
  1. +1 −0  .gitignore
  2. +164 −0 index.js
  3. +24 −0 package.json
  4. +303 −0 tests.js
1  .gitignore
@@ -0,0 +1 @@
+node_modules
164 index.js
@@ -0,0 +1,164 @@
+
+var prime = require('prime');
+var type = require('prime/util/type');
+var array = require('prime/es5/array');
+
+// Cloning objects and arrays
+var cloneOf = function(item){
+ switch (type(item)){
+ case 'array' : return cloneArray(item);
+ case 'object' : return cloneObject(item);
+ default : return item;
+ }
+};
+
+var cloneObject = function(object){
+ var clone = {};
+ for (var key in object) clone[key] = cloneOf(object[key]);
+ return clone;
+};
+
+var cloneArray = function(array){
+ var i = this.length, clone = new Array(i);
+ while (i--) clone[i] = cloneOf(this[i]);
+ return clone;
+};
+
+// reset function to reset objects and arrays in the prototype
+var reset = function(object){
+ for (var key in object){
+ var value = object[key];
+ switch (type(value)){
+ case 'object' : object[key] = reset(prime.create(value)); break;
+ case 'array' : object[key] = cloneArray(value); break;
+ }
+ }
+ return object;
+};
+
+// wrap methods to be able to use .parent()
+var wrap = function(fn, name, proto){
+
+ // unwrap if method was already wrapped
+ if (fn._origin) fn = fn._origin;
+
+ var wrapped = function(){
+
+ var _name = this._callName;
+ var _proto = this._callProto;
+
+ this._callName = name;
+ this._callProto = proto;
+
+ var result = fn.apply(this, arguments);
+
+ this._callName = _name;
+ this._callProto = _proto;
+
+ return result;
+
+ };
+
+ wrapped._origin = fn;
+
+ return wrapped;
+
+};
+
+var Class = prime({
+
+ constructor: function(){
+ reset(this);
+ // initialize is wrapped, so can use .parent(). .constructor however isn't wrapped.
+ if (this.initialize) this.initialize.apply(this, arguments);
+ },
+
+ mutator: function(key, method){
+
+ if (key === 'mixin'){
+
+ switch (type(method)){
+ case "function" : this.implement(prime.create(method.prototype)); break;
+ case "array" : array.forEach(method, function(v){this.implement({"mixin": v})}, this); break;
+ case "object" : this.implement(method); break;
+ }
+
+ } else if (typeof method === 'function' && key != 'parent'){
+
+ this.prototype[key] = wrap(method, key, this.prototype);
+
+ } else {
+
+ this.prototype[key] = method;
+
+ }
+ },
+
+ parent: function(){
+
+ var callProto = this._callProto;
+ var name = this._callName;
+ var proto = callProto.constructor.parent;
+
+ if (typeof proto[name] != 'function') throw new Error('parent function "' + name + '" does not exist');
+
+ return proto[name].apply(this, arguments);
+
+ }
+
+});
+
+// Check of an object is a subclass of another prime
+var isSubPrimeOf = function(object, prime){
+ do {
+ if (object === Class) return true;
+ object = object && object.parent && object.parent.constructor;
+ } while (object);
+ return false;
+};
+
+var classy = function(proto){
+
+ // accept empty proto, or if the proto is a function, use that as constructor (like MooTools 1.x)
+ if (!proto) proto = {};
+ else if (typeof proto == 'function') proto = {constructor: proto};
+
+ // alias old Class keys
+
+ if (proto.Extends){
+ proto.inherits = proto.Extends;
+ delete proto.Extends;
+ }
+
+ if (proto.Implements){
+ proto.mixin = proto.Implements;
+ delete proto.Implements;
+ }
+
+ // if it will inherit from another class, that class should be a subclass of Class
+ if (proto.inherits && !isSubPrimeOf(proto.inherits, Class)) throw new Error('Inherited class should be classyfied');
+ if (!proto.inherits) proto.inherits = Class;
+
+ var prim = prime(proto);
+
+ // overload implement method
+ var implement = prim.implement;
+ prim.implement = function(name, method){
+
+ if (typeof name === 'string'){
+ var object = {};
+ object[name] = method;
+ return implement.call(this, object);
+ }
+
+ return implement.call(this, name);
+
+ };
+
+ return prim;
+
+};
+
+classy.prototype = Class.prototype;
+
+module.exports = classy;
24 package.json
@@ -0,0 +1,24 @@
+{
+ "author": "Arian Stolwijk (http://github.com/arian)",
+ "name": "Classy",
+ "description": "Brings the MooTools 1.x Class sugar to Prime",
+ "version": "0.0.1",
+ "main": "index",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/arian/classy.git"
+ },
+ "license": "MIT",
+ "dependencies": {},
+ "devDependencies": {
+ "expect.js": "0.1",
+ "mocha": "1.0"
+ },
+ "optionalDependencies": {},
+ "scripts": {
+ "test": "./node_modules/mocha/bin/mocha tests.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+}
303 tests.js
@@ -0,0 +1,303 @@
+
+var expect = require('expect.js');
+var Class = require('./index');
+
+var Animal = new Class({
+
+ initialized: false,
+
+ initialize: function(name, sound){
+ this.name = name;
+ this.sound = sound || '';
+ this.initialized = true;
+ },
+
+ eat: function(){
+ return 'animal:eat:' + this.name;
+ },
+
+ say: function(){
+ return 'animal:say:' + this.name;
+ }
+
+});
+
+var Cat = new Class({
+
+ Extends: Animal,
+
+ ferocious: false,
+
+ initialize: function(name, sound){
+ this.parent(name, sound || 'miao');
+ },
+
+ eat: function(){
+ return 'cat:eat:' + this.name;
+ },
+
+ play: function(){
+ return 'cat:play:' + this.name;
+ }
+
+});
+
+var Lion = new Class({
+
+ Extends: Cat,
+
+ ferocious: true,
+
+ initialize: function(name){
+ this.parent(name, 'rarr');
+ },
+
+ eat: function(){
+ return 'lion:eat:' + this.name;
+ }
+
+});
+
+var Actions = new Class({
+
+ jump: function(){
+ return 'actions:jump:' + this.name;
+ },
+
+ sleep: function(){
+ return 'actions:sleep:' + this.name;
+ }
+
+});
+
+var Attributes = new Class({
+
+ color: function(){
+ return 'attributes:color:' + this.name;
+ },
+
+ size: function(){
+ return 'attributes:size:' + this.name;
+ }
+
+});
+
+
+describe('Class creation', function(){
+
+ it("should call initialize upon instantiation", function(){
+ var animal = new Animal('lamina');
+ expect(animal.name).to.equal('lamina');
+ expect(animal.initialized).to.be(true);
+ expect(animal.say()).to.equal('animal:say:lamina');
+ });
+
+ it("should use 'Extend' property to extend another class", function(){
+ var cat = new Cat('fluffy');
+ expect(cat.name).to.equal('fluffy');
+ expect(cat.sound).to.equal('miao');
+ expect(cat.ferocious).to.be(false);
+ expect(cat.say()).to.equal('animal:say:fluffy');
+ expect(cat.eat()).to.equal('cat:eat:fluffy');
+ expect(cat.play()).to.equal('cat:play:fluffy');
+ });
+
+ it("should use 'Extend' property to extend an extended class", function(){
+ var leo = new Lion('leo');
+ expect(leo.name).to.equal('leo');
+ expect(leo.sound).to.equal('rarr');
+ expect(leo.ferocious).to.be(true);
+ expect(leo.say()).to.equal('animal:say:leo');
+ expect(leo.eat()).to.equal('lion:eat:leo');
+ expect(leo.play()).to.equal('cat:play:leo');
+ });
+
+ it("should use 'Implements' property to implement another class", function(){
+ var Dog = new Class({
+ Implements: Animal
+ });
+
+ var rover = new Dog('rover');
+ expect(rover.name).to.equal('rover');
+ expect(rover.initialized).to.be(true);
+ expect(rover.eat()).to.equal('animal:eat:rover');
+ });
+
+ it("should use 'Implements' property to implement any number of classes", function(){
+ var Dog = new Class({
+ Extends: Animal,
+ Implements: [Actions, Attributes]
+ });
+
+ var rover = new Dog('rover');
+ expect(rover.initialized).to.be(true);
+ expect(rover.eat()).to.equal('animal:eat:rover');
+ expect(rover.say()).to.equal('animal:say:rover');
+ expect(rover.jump()).to.equal('actions:jump:rover');
+ expect(rover.sleep()).to.equal('actions:sleep:rover');
+ expect(rover.size()).to.equal('attributes:size:rover');
+ expect(rover.color()).to.equal('attributes:color:rover');
+ });
+
+ it("should alter the Class's prototype when implementing new methods", function(){
+ var Dog = new Class({
+ Extends: Animal
+ });
+
+ var rover = new Dog('rover');
+
+ Dog.implement({
+ jump: function(){
+ return 'dog:jump:' + this.name;
+ }
+ });
+
+ var spot = new Dog('spot');
+
+ expect(spot.jump()).to.equal('dog:jump:spot');
+ expect(rover.jump()).to.equal('dog:jump:rover');
+ });
+
+ it("should alter the Class's prototype when implementing new methods into the super class", function(){
+ var Dog = new Class({
+ Extends: Animal
+ });
+
+ var rover = new Dog('rover');
+
+ Animal.implement({
+ jump: function(){
+ return 'animal:jump:' + this.name;
+ }
+ });
+
+ var spot = new Dog('spot');
+
+ expect(spot.jump()).to.equal('animal:jump:spot');
+ expect(rover.jump()).to.equal('animal:jump:rover');
+ });
+
+ it("should alter the Class's prototype when overwriting methods in the super class", function(){
+ var Dog = new Class({
+ Extends: Animal
+ });
+
+ var rover = new Dog('rover');
+ expect(rover.say()).to.equal('animal:say:rover');
+
+ Animal.implement({
+ say: function(){
+ return 'NEW:animal:say:' + this.name;
+ }
+ });
+
+ var spot = new Dog('spot');
+
+ expect(spot.say()).to.equal('NEW:animal:say:spot');
+ expect(rover.say()).to.equal('NEW:animal:say:rover');
+ });
+
+});
+
+describe('Class::implement', function(){
+
+ it('should implement an object', function(){
+ var Dog = new Class({
+ Extends: Animal
+ });
+
+ Dog.implement(new Actions());
+
+ var rover = new Dog('rover');
+
+ expect(rover.name).to.equal('rover');
+ expect(rover.jump()).to.equal('actions:jump:rover');
+ expect(rover.sleep()).to.equal('actions:sleep:rover');
+ });
+
+ it('should implement any number of objects', function(){
+ var Dog = new Class({
+ Extends: Animal
+ });
+
+ Dog.implement(new Actions()).implement(new Attributes());
+
+ var rover = new Dog('rover');
+
+ expect(rover.name).to.equal('rover');
+ expect(rover.jump()).to.equal('actions:jump:rover');
+ expect(rover.sleep()).to.equal('actions:sleep:rover');
+ expect(rover.size()).to.equal('attributes:size:rover');
+ expect(rover.color()).to.equal('attributes:color:rover');
+ });
+
+ it('should implement key-value objects', function(){
+ var Dog = new Class({
+ Extends: Animal
+ });
+
+ Dog.implement({
+ bark: function(){
+ return 'woof!';
+ },
+ jump: function(){
+ return 'jump';
+ }
+ });
+
+ var rover = new Dog('rover');
+
+ expect(rover.bark()).to.equal('woof!');
+ expect(rover.jump()).to.equal('jump');
+ });
+
+ it('should implement a new method', function(){
+ var Dog = new Class({
+ Extends: Animal
+ });
+
+ Dog.implement('bark', function(){
+ return 'woof!';
+ }).implement('jump', function(){
+ return 'jump';
+ });
+
+ var rover = new Dog('rover');
+
+ expect(rover.bark()).to.equal('woof!');
+ expect(rover.jump()).to.equal('jump');
+ });
+
+});
+
+describe('Class toString', function(){
+
+ it('should allow to implement toString', function(){
+ var Person = new Class({
+
+ initialize: function(name){
+ this.name = name;
+ },
+
+ toString: function(){
+ return this.name;
+ }
+
+ });
+
+ var Italian = new Class({
+
+ Extends: Person,
+
+ toString: function(){
+ return "It's me, " + this.name;
+ }
+
+ });
+
+ expect((new Person('Valerio')) + '').to.be('Valerio');
+
+ expect((new Italian('Valerio')) + '').to.be("It's me, Valerio");
+ });
+
+});
Please sign in to comment.
Something went wrong with that request. Please try again.