Skip to content

Commit

Permalink
Added tests and vendor dir. Added new version of Classic, which repla…
Browse files Browse the repository at this point in the history
…ces bridge function with an Object.create polyfill, which is much more useful.
  • Loading branch information
gordonbrander committed Feb 9, 2012
1 parent febe67b commit a583854
Show file tree
Hide file tree
Showing 11 changed files with 3,121 additions and 0 deletions.
136 changes: 136 additions & 0 deletions classic.amd.js
@@ -0,0 +1,136 @@
define(function () {
// Fast slice lookup.
var slice = Array.prototype.slice;

// Merge any number of objects together.
// The first object has it's reference modified
// (doesn't return a new object).
// The last object property wins.
//
// Serves the same purpose as jQuery.extend and _.extend.
var merge = function (obj) {
var objects = slice.call(arguments, 1),
key, i, l, objN;

for (i=0, l=objects.length; i < l; i++) {
objN = objects[i];
for (key in objN) {
// Don't copy built-in or inherited object properties.
if (!objN.hasOwnProperty(key)) continue;
obj[key] = objN[key];
}
}

return obj;
};

// Use a provided object as the prototype for
// another object.
// Delegates to Object.create, if supported.
var create = Object.create || function (obj) {
function Ctor() {}
Ctor.prototype = obj;
return new Ctor();
};

// Minimal classical inheritance via object literals.
// Inspired by jashkenas' Proposal: <https://gist.github.com/1329619>.
//
// Creates constructor functions from objects.
// All "own" properties of the passed objects will be copied to the
// prototype. If the first argument is another constructor function, a
// prototype bridge will be created between the constructor function
// provided and the resulting child constructor function.
// This gives you classical inheritance via efficient prototype chaining,
// without emulating `super` or `static`.
//
// Check it out:
//
// var Bunny = classic({
// hop: function (length) { ... }
// });
//
// var JackRabbit = classic(Bunny, {
// constructor: function (type) {
// this.type = type;
// },
// skip: function (length) { ... }
// });
//
// var myJackRabbit = new Jackrabbit('grey');
// myJackRabbit.hop();
// myJackRabbit.skip();
//
// Also supported: multiple inheritance via any number of object mixins:
//
// var person = { ... };
// var musician = { ... };
// var writer = { ... };
// var Composer = classic(person, musician, writer);
//
// var myComposer = new Composer();
//
// When more than one object is passed in, the objects are merged
// The last mentioned property wins, as you might expect.
//
// Want to do both? Go for it. The first property can be a constructor
// function, with any number of objects passed in after.
//
// var BrownBear = classic(Animal, bear, best);
var classic = function (Parent) {
// If the first param is a function, consider it the parent "class".
// Cache this test, since we use it more than once.
var hasParent = ('function' === typeof Parent),
// Determine where to slice the arguments. If the first
// argument is a constructor function,
// we want to slice at `1`, so it is not included in the object merge.
// If the first argument is not a function, assume it is an object
// and include it in the object merge.
at = (hasParent ? 1 : 0),
rest = slice.call(arguments, at),
obj, Child, __Child;

// If we've got more than one object, merge all "rest" objects into a
// single object, creating a shallow copy so we don't accidentally modify
// objects passed in.
obj = (rest.length > 1) ? merge.apply(null, [{}].concat(rest)) : rest[0];

// Create constructor using:
//
// * `constructor` property of object, if set
// * OR patch in parent constructor if a parent has been passed in
// * OR use an empty function if no parent is assigned.
Child = (
obj.hasOwnProperty('constructor') ?
obj.constructor :
(
hasParent ?
function () {
return Parent.apply(this, arguments);
} :
function () {}
)
);

// Make a prototype bridge between child and parent.
if (hasParent) Child.prototype = create(Parent.prototype);

// Merge properties from our object literal into the prototype of our
// constructor. Properties in our object will obscure properties inherited
// from the `Parent` prototype.
//
// After merging, set the fully finished constructor function (with
// prototype) as the constructor property of the prototype.
// Putting a constructor property on the prototype will
// guaranteed you have one, even if the browser does not set it during
// construction (**cough, IE, cough**).
merge(Child.prototype, obj).constructor = Child;
return Child;
};

// Expose these handy helper methods.
classic.create = create;
classic.merge = merge;

return classic;
});
Binary file added test/.DS_Store
Binary file not shown.
27 changes: 27 additions & 0 deletions test/runner.html
@@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="stylesheet" href="vendor/jasmine.css">
<script src="../vendor/require.min.js"></script>
<script src="vendor/jasmine.js"></script>
<script src="vendor/jasmine-html.js"></script>
<script>
require.config({
// configure our AMD loader
baseUrl: '../'
});

require([
'test/spec/classic.spec'
],
function(){
jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
jasmine.getEnv().execute();
});
</script>
</head>

<body>
</body>
</html>
Binary file added test/spec/.DS_Store
Binary file not shown.
75 changes: 75 additions & 0 deletions test/spec/classic.spec.js
@@ -0,0 +1,75 @@
define(['classic.amd'], function (classic) {
describe('classic', function () {
var Animal, Bunny, Jackrabbit,
animal, bunny, jackrabbit;

Animal = classic({
run: function () { }
});

Bunny = classic(Animal, {
constructor: function (color) {
this.color = color;
},
hop: function () { }
});

Jackrabbit = classic(Bunny, {
skip: function () { }
});

beforeEach(function () {
animal = new Animal();
bunny = new Bunny();
jackrabbit = new Jackrabbit();
});

it('is a function', function () {
expect(typeof classic).toBe('function');
});

it('returns a constructor function', function () {
expect(typeof Animal).toBe('function');
expect(animal instanceof Animal).toBeTruthy();
});

it('will create an empty constructor if one is not provided',
function() {
expect(typeof Animal.prototype.constructor).toBe('function');
});

it('constructed objects have the fully-formed constructor function assigned to the constructor property of their prototype',
function () {
expect(typeof bunny.constructor).toBe('function');
expect(bunny.constructor.prototype).toBe(Bunny.prototype);
});

it('constructs objects that inherit properties from ancestors',
function () {
console.log(bunny, jackrabbit);

expect(typeof bunny.run).toBe('function');
expect(typeof jackrabbit.hop).toBe('function');
expect(typeof jackrabbit.run).toBe('function');
});

it('properties inherited from a constructor function do so by prototype bridge rather than object copy.',
function () {
expect(Jackrabbit.prototype instanceof Bunny).toBeTruthy();
expect(jackrabbit.hasOwnProperty('run')).toBeFalsy();
expect(jackrabbit.hasOwnProperty('hop')).toBeFalsy();
});

it('constructor functions can be created by mixing multiple objects', function () {
var musician = { play: function () {} };
var person = { think: function () {} };

var Composer = classic(person, musician);
});

it('will call the parent\'s constructor if one is not supplied',
function() {
// TODO
});
});
});
20 changes: 20 additions & 0 deletions test/vendor/MIT.LICENSE
@@ -0,0 +1,20 @@
Copyright (c) 2008-2011 Pivotal Labs

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

0 comments on commit a583854

Please sign in to comment.