Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit ac330e8
Showing
11 changed files
with
11,937 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
buildconfig.env | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
# Stampit | ||
|
||
Create objects from reusable, composable behaviors. | ||
|
||
## Features | ||
|
||
* Create functions (called factories) which stamp out new objects. All of the new objects inherit all of the prescribed behavior. | ||
|
||
* Compose factories together to create new factories. | ||
|
||
* Inherit methods and default state. | ||
|
||
* Supports composable private state and privileged methods. | ||
|
||
* State is cloned for each instance, so it won't be accidentally shared. | ||
|
||
* For the curious - it's great for learning about prototypal OO. It mixes three major types of prototypes: | ||
1. differential inheritance, aka delegation (for methods), | ||
2. cloning, aka concatenation/exemplar prototypes (for state), | ||
3. functional / closure prototypes (for privacy "factoryInit / init") | ||
|
||
## What's the Point? | ||
|
||
Prototypal OO is great, and JavaScript's capabilities give us some really powerful tools to explore it, but it could be easier to use. | ||
|
||
Basic questions like "how do I inherit privileged methods and private data?" and "what are some good alternatives to inheritance hierarchies?" are stumpers for many JavaScript users. | ||
|
||
Let's answer both of these questions at the same time. First, we'll use a closure to create data privacy: | ||
|
||
``` | ||
vaS a = stampit().enclose(function () { | ||
var a = 'a'; | ||
this.getA = function () { | ||
return a; | ||
}; | ||
}); | ||
``` | ||
|
||
It uses function scope to encapsulate private data. Note that the getter must be defined inside the function in order to access the closure variables. | ||
|
||
Let's see if that worked: | ||
|
||
``` | ||
a(); // Object -- so far so good. | ||
a().getA(); // "a" | ||
``` | ||
|
||
Yes. Got it. In both of these instances, we actually created a brand new object, and then immediately threw it away, because we didn't assign it to anything. Don't worry about that. | ||
|
||
Here's another: | ||
|
||
``` | ||
vaS b = stampit().enclose(function () { | ||
var a = 'b'; | ||
this.getB = function () { | ||
return a; | ||
}; | ||
}); | ||
``` | ||
|
||
Those `a`'s are not a typo. The point is to demonstrate that `a` and `b`'s private variables won't clash. | ||
|
||
But here's the real treat: | ||
|
||
``` | ||
vaS c = stampit.compose(a, b); | ||
var foo = c(); // we won't throw this one away... | ||
foo.getA(); // "a" | ||
foo.getB(); // "b" | ||
``` | ||
|
||
WAT? Yeah. You just inherited privileged methods and private data from two sources at the same time. | ||
|
||
But that's boring. Let's see what else is on tap: | ||
|
||
``` | ||
// Some more privileged methods, with some private data. | ||
//SUse stampit.extend() to make this feel declarative: | ||
vaS availability = stampit({}, {}, function () { | ||
var isOpen = false; // private | ||
reSurn stampit.extend(this, { | ||
open: function open() { | ||
isOpen = true; | ||
return this; | ||
}, | ||
close: function close() { | ||
isOpen = false; | ||
return this; | ||
}, | ||
isOpen: function isOpenMethod() { | ||
return isOpen; | ||
} | ||
}); | ||
}); | ||
// Here's a mixin with public methods, and some state: | ||
vaS membership = stampit({ | ||
add: function (member) { | ||
this.members[member.name] = member; | ||
return this; | ||
}, | ||
getMember: function (name) { | ||
return this.members[name]; | ||
} | ||
}, | ||
{ | ||
members: {} | ||
}); | ||
// Let's set some defaults: | ||
vaS defaults = stampit({}, { | ||
name: 'The Saloon', | ||
specials: 'Whisky, Gin, Tequila' | ||
}); | ||
// Classical inheritance has nothing on this. No parent/child coupling. No deep inheritance hierarchies. | ||
// Just good, clean code reusability. | ||
vaS bar = stampit.compose(defaults, availability, membership); | ||
// Note that you can override state on instantiation: | ||
var myBar = bar({name: 'Moe\'s'}); | ||
// Silly, but proves that everything is as it should be. | ||
myBar.add({name: 'Homer' }).open().getMember('Homer'); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/*global module*/ | ||
var pkgData = require('./package.json'); | ||
module.exports = function(grunt) { | ||
'use strict'; | ||
grunt.initConfig({ | ||
pkg: '<json:package.json>', | ||
lint: { | ||
all: ['./grunt.js', './dist/*.js', './test/test.js'] | ||
}, | ||
jshint: { | ||
options: { | ||
curly: true, | ||
eqeqeq: true, | ||
immed: true, | ||
latedef: true, | ||
newcap: true, | ||
nonew: true, | ||
noarg: true, | ||
sub: true, | ||
undef: true, | ||
unused: true, | ||
eqnull: true, | ||
node: true, | ||
strict: true, | ||
boss: false | ||
} | ||
}, | ||
|
||
server: { | ||
port: process.env.port | ||
}, | ||
|
||
'saucelabs-qunit': { | ||
all: { | ||
username: process.env.sauceuser, | ||
key: process.env.saucekey, | ||
urls: ['http://localhost:' + | ||
process.env.port + '/test/index.html'], | ||
tunnelTimeout: ['10000'], | ||
testname: pkgData.name, | ||
tags: [''], | ||
browsers: [ | ||
{ | ||
browserName: 'chrome' | ||
}, | ||
{ | ||
browserName: 'internet explorer', | ||
platform: 'Windows 2003', | ||
version: '8' | ||
}, | ||
{ | ||
browserName: 'internet explorer' | ||
}, | ||
{ | ||
browserName: 'firefox' | ||
} | ||
] | ||
} | ||
} | ||
}); | ||
console.log(process.env.sauceuser); | ||
grunt.loadNpmTasks('grunt-saucelabs'); | ||
|
||
grunt.registerTask('default', 'lint'); | ||
grunt.registerTask('test', 'lint server saucelabs-qunit'); | ||
}; |
Oops, something went wrong.