-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathmethods.js
executable file
·145 lines (97 loc) · 4.01 KB
/
methods.js
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Load modules
var Boom = require('boom');
var Catbox = require('catbox');
var Hoek = require('hoek');
var Schema = require('./schema');
// Declare internals
var internals = {};
exports = module.exports = internals.Methods = function (pack) {
this.pack = pack;
this.methods = {};
};
internals.Methods.prototype.add = function (/* name, fn, options, env | {}, env | [{}, {}], env */) {
if (typeof arguments[0] === 'string') {
return internals.Methods.prototype._add.apply(this, arguments);
}
var items = [].concat(arguments[0]);
var env = arguments[1];
for (var i = 0, il = items.length; i < il; ++i) {
var item = items[i];
this._add(item.name, item.fn, item.options, env);
}
};
exports.methodNameRx = /^[a-zA-Z]\w*(?:\.[a-zA-Z]\w*)*$/;
internals.Methods.prototype._add = function (name, fn, options, env) {
var self = this;
Hoek.assert(typeof fn === 'function', 'fn must be a function');
Hoek.assert(typeof name === 'string', 'name must be a string');
Hoek.assert(name.match(exports.methodNameRx), 'Invalid name:', name);
Hoek.assert(!Hoek.reach(this.methods, name, { functions: false }), 'Server method function name already exists');
options = options || {};
Schema.assert('method', options, name);
var settings = Hoek.cloneWithShallow(options, ['bind']);
settings.generateKey = settings.generateKey || internals.generateKey;
var bind = settings.bind || (env && env.bind) || null;
// Create method
if (!settings.cache) {
this._assign(name, function (/* arguments, methodNext */) {
var args = arguments;
var methodNext = args[args.length - 1];
var timer = new Hoek.Timer();
args[args.length - 1] = function (err, result) {
methodNext(err, result, null, { msec: timer.elapsed(), error: err });
};
fn.apply(bind, args);
});
return;
}
settings.cache.generateFunc = function (id, next) {
id.args[id.args.length - 1] = next; // function (err, result, ttl)
fn.apply(bind, id.args);
};
var cache = this.pack._provisionCache(settings.cache, 'method', name, settings.cache.segment);
var method = function (/* arguments, methodNext */) {
var args = arguments;
var methodNext = args[args.length - 1];
var key = settings.generateKey.apply(bind, args);
if (key === null || typeof key !== 'string') { // Value can be ''
self.pack.log(['hapi', 'method', 'key', 'error'], { name: name, args: args, key: key });
key = null;
}
cache.get({ id: key, args: args }, methodNext);
};
method.cache = {
drop: function (/* arguments, callback */) {
var dropCallback = arguments[arguments.length - 1];
var key = settings.generateKey.apply(null, arguments);
if (key === null) { // Value can be ''
return Hoek.nextTick(dropCallback)(Boom.badImplementation('Invalid method key'));
}
return cache.drop(key, dropCallback);
}
};
this._assign(name, method);
};
internals.Methods.prototype._assign = function (name, method) {
var path = name.split('.');
var ref = this.methods;
for (var i = 0, il = path.length; i < il; ++i) {
if (!ref[path[i]]) {
ref[path[i]] = (i + 1 === il ? method : {});
}
ref = ref[path[i]];
}
};
internals.generateKey = function () {
var key = 'h'; // 'h' for helper
for (var i = 0, il = arguments.length - 1; i < il; ++i) { // 'arguments.length - 1' to skip 'next'
var arg = arguments[i];
if (typeof arg !== 'string' &&
typeof arg !== 'number' &&
typeof arg !== 'boolean') {
return null;
}
key += ':' + encodeURIComponent(arg.toString());
}
return key;
};