-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathi.js
More file actions
127 lines (103 loc) · 3.94 KB
/
i.js
File metadata and controls
127 lines (103 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
'use strict';
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
define(['underscore'], function (_) {
function noOp() {}
function addFunctionNameToObject(functionName, object) {
if (functionName && _.isString(functionName)) {
object[functionName] = 'function';
}
}
function processBlock(block, blockName, outObject) {
if (_.isArray(block[blockName])) {
outObject[blockName] = outObject[blockName] || {};
_.each(block[blockName], function (value) {
addFunctionNameToObject(value, outObject[blockName]);
});
} else if (_.isObject(block[blockName])) {
outObject[blockName] = outObject[blockName] || {};
_.each(block[blockName], function (value, key) {
addFunctionNameToObject(key, outObject[blockName]);
});
}
}
function buildInterface(interfaceDescription) {
var result = {
required: {}
};
if (_.isArray(interfaceDescription)) {
_.each(interfaceDescription, function (methodName) {
addFunctionNameToObject(methodName, result.required);
});
} else if (_.isObject(interfaceDescription)) {
processBlock(interfaceDescription, 'required', result);
processBlock(interfaceDescription, 'optional', result);
}
return result;
}
var Methodical = function (interfaceDescription) {
this._interface = buildInterface(interfaceDescription);
};
Methodical.function = 'function';
Methodical.fromConstructor = function (Constructor) {
var requiredMethods;
if (_.isFunction(Constructor)) {
requiredMethods = [];
_.each(Constructor.prototype, function (value, key) {
if (_.isFunction(value)) {
requiredMethods.push(key);
}
});
} else {
throw new TypeError('A function must be passed');
}
return new Methodical(requiredMethods);
};
Methodical.prototype.getInterface = function () {
return this._interface;
};
Methodical.prototype.check = function (object) {
var errors = [];
// ensures that if we pass garbage in, we get sensible error messages
if (!_.isObject(object)) {
object = {};
}
_.each(this.getInterface().required, _.bind(function (value, key) {
if (typeof object[key] !== value) {
errors.push('The required method "' + key + '" is not implemented');
}
}, this));
if (errors.length) {
var throwable = new TypeError();
var messageIntro = 'The object does not conform to the interface: ';
if (this.name) {
messageIntro = 'The object does not conform to the "' + this.name + '" interface: ';
}
throwable.message = messageIntro + errors.join('; ');
throw throwable;
}
};
Methodical.prototype.complete = function (object) {
if (!_.isFunction(object) && !_.isObject(object)) {
throw new TypeError('Cannot complete a non-object');
}
_.each(this.getInterface().required, _.bind(function (value, key) {
if (typeof object[key] !== value) {
object[key] = noOp;
}
}, this));
};
Methodical.prototype.tryCall = function (object, method) {
var methodArguments = Array.prototype.slice.call(arguments, 2);
this.tryApply(object, method, methodArguments);
};
Methodical.prototype.tryApply = function (object, method, args) {
if (_.isFunction(object[method])) {
object[method].apply(object, args);
} else if (this.getInterface().required[method] === 'function') {
object[method].apply(object, args);
}
};
return Methodical;
});