Skip to content

Commit ae6c05d

Browse files
committed
First and full commit for v0.0.1
1 parent 1592ded commit ae6c05d

File tree

5 files changed

+404
-0
lines changed

5 files changed

+404
-0
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
test:
2+
./node_modules/.bin/mocha test.js

README.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# ProtocolJS
2+
3+
ProtocolJS ports the concept of protocols found in Clojure to JavaScript.
4+
5+
## Inspiration and Attribution
6+
7+
ProtocolJS is an informal fork of [Gozala's protocol module](https://github.com/Gozala/protocol)
8+
with some variation in implementation to
9+
10+
- Make it compatible across more browsers.
11+
- Make it more robust to using serialized objects with the library between runtime restarts.
12+
- Reflect small differences in coding style.
13+
14+
## Installation
15+
16+
```
17+
$ npm install protocoljs
18+
```
19+
20+
## Documentation
21+
22+
A protocol is a set of function interfaces.
23+
24+
```javascript
25+
var protocol = require('protocoljs');
26+
27+
var Enumerable = protocol({
28+
first: [protocol] // Here protocol is a placeholder for the typed argument
29+
, rest: [protocol]
30+
, repeat: [Number, protocol] // The typed argument does not need to be first
31+
});
32+
```
33+
34+
Protocols enable polymorphism in a flexible way where the interface definition
35+
is decoupled from the implementations for any number of types.
36+
37+
```javascript
38+
Enumerable(Array, {
39+
first: function (array) {
40+
return array[0];
41+
}
42+
, rest: function (array) {
43+
return array.slice(1);
44+
}
45+
, repeat: function (times, array) {
46+
var result = [];
47+
while (times--) {
48+
result = result.concat(array);
49+
}
50+
return result;
51+
}
52+
});
53+
54+
Enumerable.first([1, 2, 3]); // => 1
55+
Enumerable.rest([1, 2, 3]); // => [2, 3]
56+
Enumerable.repeat(2, [1, 2, 3]); // => [1, 2, 3, 1, 2, 3]
57+
```
58+
59+
You can tie any number of types to a protocol.
60+
61+
```javascript
62+
Enumerable(String, {
63+
first: function (string) {
64+
return string.charAt(0);
65+
}
66+
, rest: function (string) {
67+
return string.substring(1);
68+
}
69+
, repeat: function (times, string) {
70+
var result = '';
71+
while (times--) {
72+
result += string;
73+
}
74+
return result;
75+
}
76+
});
77+
78+
Enumerable.first('abc'); => 'a'
79+
Enumerable.rest('abc'); => 'bc'
80+
Enumerable.repeat(2, 'abc'); => 'abcabc'
81+
```
82+
83+
You can also tie any number of protocols to a type.
84+
85+
```javascript
86+
var RightEnumerable = protocol({
87+
first: [protocol]
88+
, rest: [protocol]
89+
});
90+
91+
RightEnumerable(String, {
92+
first: function (string) {
93+
return string.charAt(string.length-1);
94+
}
95+
, rest: function (string) {
96+
return string.substring(0, string.length-1);
97+
}
98+
});
99+
100+
RightEnumerable.first('abc'); // => 'c'
101+
RightEnumerable.rest('abc'); // => 'ab'
102+
```
103+
104+
Finally, you can re-open protocols and extend their interfaces.
105+
106+
```javascript
107+
protocol(Enumerable, {
108+
slice: [protocol, Number, Number]
109+
});
110+
111+
Enumerable(Array, {
112+
slice: function (array, start, end) {
113+
return array.slice(start, end);
114+
}
115+
});
116+
117+
Enumerable(String, {
118+
slice: function (string, start, end) {
119+
return string.substring(start, end);
120+
}
121+
});
122+
123+
Enumerable.slice([1, 2, 3], 0, 2); => [1, 2]
124+
Enumerable.slice('abc', 0, 2); => 'ab'
125+
```
126+
127+
### MIT License
128+
Copyright (c) 2011 by Brian Noguchi and Nate Smith
129+
130+
Permission is hereby granted, free of charge, to any person obtaining a copy
131+
of this software and associated documentation files (the "Software"), to deal
132+
in the Software without restriction, including without limitation the rights
133+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
134+
copies of the Software, and to permit persons to whom the Software is
135+
furnished to do so, subject to the following conditions:
136+
137+
The above copyright notice and this permission notice shall be included in
138+
all copies or substantial portions of the Software.
139+
140+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
141+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
142+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
143+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
144+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
145+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
146+
THE SOFTWARE.

index.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
module.exports = defineProtocol;
2+
3+
/**
4+
* @param {Function} @optional protocol
5+
* @param {Object} _interface is an object whose keys are the function names
6+
* and whose values are an array representing the arguments signature
7+
*/
8+
function defineProtocol (protocol, _interface) {
9+
if (arguments.length === 1) {
10+
_interface = protocol;
11+
var protocol = function (type, implementations) {
12+
if (typeof type === 'function') {
13+
var typePrototype = type.prototype;
14+
}
15+
var types = protocol[':types'];
16+
var typeStr = typeOf(type && Object.create(typePrototype))
17+
, typeFns = types[typeStr];
18+
19+
if (! typeFns) {
20+
var protocolId = typePrototype.protocolId
21+
, Other = types.Other;
22+
typeFns = Other[protocolId] || (Other[protocolId] = {});
23+
}
24+
Object.keys(implementations).forEach( function (methodName) {
25+
if (! (methodName in protocol)) {
26+
throw new Error('This protocol does not include "' + methodName + '"');
27+
}
28+
if (methodName in typeFns) {
29+
throw new Error('The type already implements the protocol method ' + methodName);
30+
}
31+
typeFns[methodName] = implementations[methodName];
32+
});
33+
}
34+
protocol[':interface'] = _interface;
35+
protocol[':types'] = {
36+
'Arguments': {}
37+
, 'Array': {}
38+
, 'Boolean': {}
39+
, 'Date': {}
40+
, 'Function': {}
41+
, 'Null': {}
42+
, 'Number': {}
43+
, 'Other': {} // For any constructors
44+
, 'RegExp': {}
45+
, 'String': {}
46+
, 'Undefined': {}
47+
};
48+
} else {
49+
for (var k in _interface) {
50+
protocol[':interface'][k] = _interface[k];
51+
}
52+
}
53+
54+
extendProtocol(protocol, _interface);
55+
56+
return protocol;
57+
}
58+
59+
function extendProtocol (protocol, _interface) {
60+
Object.keys(_interface).forEach( function (methodName) {
61+
var index = _interface[methodName].indexOf(defineProtocol)
62+
, types = protocol[':types'];
63+
protocol[methodName] = function method () {
64+
var target = arguments[index]
65+
, typeStr = typeOf(target)
66+
, typeFns = types[typeStr]
67+
, fn = (typeFns)
68+
? typeFns[methodName]
69+
: types.Other[target.protocolId][methodName];
70+
return fn.apply(null, arguments);
71+
};
72+
});
73+
}
74+
75+
function typeOf (x) {
76+
var type = Object.prototype.toString.call(x).split(' ')[1].split(']')[0];
77+
return type === 'Object' ? x.constructor.name : type;
78+
}

package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"author": "Brian Noguchi <[email protected]>",
3+
"name": "protocoljs",
4+
"description": "Clojure-style protocols in JavaScript",
5+
"version": "0.0.1",
6+
"homepage": "https://github.com/codeparty/protocoljs",
7+
"repository": {
8+
"url": "git://github.com/codeparty/protocoljs.git"
9+
},
10+
"main": "./index.js",
11+
"scripts": {
12+
"test": "make test"
13+
},
14+
"dependencies": {},
15+
"devDependencies": {
16+
"expect.js": "*",
17+
"mocha": "*"
18+
},
19+
"optionalDependencies": {},
20+
"engines": {
21+
"node": "*"
22+
}
23+
}

0 commit comments

Comments
 (0)