Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
javascript-journal committed Feb 10, 2013
0 parents commit ac330e8
Show file tree
Hide file tree
Showing 11 changed files with 11,937 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -0,0 +1,2 @@
buildconfig.env
node_modules
128 changes: 128 additions & 0 deletions README.md
@@ -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');
```
66 changes: 66 additions & 0 deletions grunt.js
@@ -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');
};

0 comments on commit ac330e8

Please sign in to comment.