UNPKG

14.1 kBJavaScriptView Raw
1var Random = require('./random'),
2 Util = require('./util');
3
4var Mock = module.exports = {
5 _mocked: {}
6};
7
8// BEGIN(BROWSER)
9/*
10 mock data
11*/
12
13/*
14 rkey
15 name|+inc
16 name|repeat
17 name|min-max
18 name|min-max.dmin-dmax
19 name|int.dmin-dmax
20
21 1 name, 2 inc, 3 range, 4 decimal
22
23 rplaceholder
24 placeholder(*)
25
26 [正则查看工具](http://www.regexper.com/)
27
28 #26 生成规则 支持 负数,例如 number|-100-100
29*/
30var rkey = /(.+)\|(?:\+(\d+)|([\+\-]?\d+-?[\+\-]?\d*)?(?:\.(\d+-?\d*))?)/,
31 rrange = /([\+\-]?\d+)-?([\+\-]?\d+)?/,
32 rplaceholder = /\\*@([^@#%&()\?\s\/\.]+)(?:\((.*?)\))?/g; // (^(?:.|\r|\n)*?)
33
34Mock.extend = Util.extend
35
36/*
37 ## Mock
38
39 ### Mock.mock( rurl?, rtype?, template|function() )
40
41 根据数据模板生成模拟数据。
42
43 * Mock.mock( template )
44 * Mock.mock( rurl, template )
45 * Mock.mock( rurl, function() )
46 * Mock.mock( rurl, rtype, template )
47 * Mock.mock( rurl, rtype, function() )
48*/
49Mock.mock = function(rurl, rtype, template) {
50 // Mock.mock(template)
51 if (arguments.length === 1) {
52 return Handle.gen(rurl)
53 }
54 // Mock.mock(rurl, template)
55 if (arguments.length === 2) {
56 template = rtype
57 rtype = undefined
58 }
59 Mock._mocked[rurl + (rtype || '')] = {
60 rurl: rurl,
61 rtype: rtype,
62 template: template
63 }
64 return Mock
65}
66
67var Handle = {
68 extend: Util.extend
69}
70
71Handle.rule = function(name) {
72 name = (name || '') + ''
73
74 var parameters = (name || '').match(rkey),
75
76 range = parameters && parameters[3] && parameters[3].match(rrange),
77 min = range && parseInt(range[1], 10), // || 1
78 max = range && parseInt(range[2], 10), // || 1
79 // repeat || min-max || 1
80 count = range ? !range[2] && parseInt(range[1], 10) || Random.integer(min, max) : 1,
81
82 decimal = parameters && parameters[4] && parameters[4].match(rrange),
83 dmin = decimal && parseInt(decimal[1], 10), // || 0,
84 dmax = decimal && parseInt(decimal[2], 10), // || 0,
85 // int || dmin-dmax || 0
86 dcount = decimal ? !decimal[2] && parseInt(decimal[1], 10) || Random.integer(dmin, dmax) : 0,
87
88 point = parameters && parameters[4];
89
90 return {
91 parameters: parameters,
92 range: range,
93 min: min,
94 max: max,
95 count: count,
96 decimal: decimal,
97 dmin: dmin,
98 dmax: dmax,
99 dcount: dcount,
100 point: point
101 }
102}
103
104/*
105 template 属性值(即数据模板)
106 name 属性名
107 context 数据上下文,生成后的数据
108 templateContext 模板上下文,
109
110 Handle.gen(template, name, options)
111 context
112 currentContext, templateCurrentContext,
113 path, templatePath
114 root, templateRoot
115*/
116Handle.gen = function(template, name, context) {
117 name = name = (name || '') + ''
118
119 context = context || {}
120 context = {
121 // 当前访问路径,只有属性名,不包括生成规则
122 path: context.path || [],
123 templatePath: context.templatePath || [],
124 // 最终属性值的上下文
125 currentContext: context.currentContext,
126 // 属性值模板的上下文
127 templateCurrentContext: context.templateCurrentContext || template,
128 root: context.root,
129 templateRoot: context.templateRoot
130 }
131 // console.log('path:', path.join('.'), template)
132
133 var rule = Handle.rule(name)
134 var type = Util.type(template)
135
136 if (Handle[type]) {
137 return Handle[type]({
138 // 属性值类型
139 type: type,
140 // 属性值模板
141 template: template,
142 // 属性名 + 生成规则
143 name: name,
144 // 属性名
145 parsedName: name ? name.replace(rkey, '$1') : name,
146
147 // 解析后的生成规则
148 rule: rule,
149 // 相关上下文
150 context: context
151 })
152 }
153 return template
154}
155
156Handle.extend({
157 array: function(options) {
158 var result = [],
159 i, j;
160 // 'arr': [{ 'email': '@EMAIL' }, { 'email': '@EMAIL' }]
161 if (!options.rule.parameters) {
162 for (i = 0; i < options.template.length; i++) {
163 options.context.path.push(i)
164 result.push(
165 Handle.gen(options.template[i], i, {
166 currentContext: result,
167 templateCurrentContext: options.template,
168 path: options.context.path
169 })
170 )
171 options.context.path.pop()
172 }
173 } else {
174 // 'method|1': ['GET', 'POST', 'HEAD', 'DELETE']
175 if (options.rule.count === 1 && options.template.length > 1) {
176 // fix #17
177 options.context.path.push(options.name)
178 result = Random.pick(Handle.gen(options.template, undefined, {
179 currentContext: result,
180 templateCurrentContext: options.template,
181 path: options.context.path
182 }))
183 options.context.path.pop()
184 } else {
185 // 'data|1-10': [{}]
186 for (i = 0; i < options.rule.count; i++) {
187 // 'data|1-10': [{}, {}]
188 j = 0
189 do {
190 // 'data|1-10': []
191 result.push(Handle.gen(options.template[j++]))
192 } while (j < options.template.length)
193 }
194 }
195 }
196 return result
197 },
198 object: function(options) {
199 var result = {},
200 keys, fnKeys, key, parsedKey, inc, i;
201
202 // 'obj|min-max': {}
203 if (options.rule.min) {
204 keys = Util.keys(options.template)
205 keys = Random.shuffle(keys)
206 keys = keys.slice(0, options.rule.count)
207 for (i = 0; i < keys.length; i++) {
208 key = keys[i]
209 parsedKey = key.replace(rkey, '$1')
210 options.context.path.push(parsedKey)
211 result[parsedKey] = Handle.gen(options.template[key], key, {
212 currentContext: result,
213 templateCurrentContext: options.template,
214 path: options.context.path
215 })
216 options.context.path.pop()
217 }
218
219 } else {
220 // 'obj': {}
221 keys = []
222 fnKeys = [] // #25 改变了非函数属性的顺序,查找起来不方便
223 for (key in options.template) {
224 (typeof options.template[key] === 'function' ? fnKeys : keys).push(key)
225 }
226 keys = keys.concat(fnKeys)
227
228 /*
229 会改变非函数属性的顺序
230 keys = Util.keys(options.template)
231 keys.sort(function(a, b) {
232 var afn = typeof options.template[a] === 'function'
233 var bfn = typeof options.template[b] === 'function'
234 if (afn === bfn) return 0
235 if (afn && !bfn) return 1
236 if (!afn && bfn) return -1
237 })
238 */
239
240 for (i = 0; i < keys.length; i++) {
241 key = keys[i]
242 parsedKey = key.replace(rkey, '$1')
243 options.context.path.push(parsedKey)
244 result[parsedKey] = Handle.gen(options.template[key], key, {
245 currentContext: result,
246 templateCurrentContext: options.template,
247 path: options.context.path
248 })
249 options.context.path.pop()
250 // 'id|+1': 1
251 inc = key.match(rkey)
252 if (inc && inc[2] && Util.type(options.template[key]) === 'number') {
253 options.template[key] += parseInt(inc[2], 10)
254 }
255 }
256 }
257 return result
258 },
259 number: function(options) {
260 var result, parts, i;
261 if (options.rule.point) { // float
262 options.template += ''
263 parts = options.template.split('.')
264 // 'float1|.1-10': 10,
265 // 'float2|1-100.1-10': 1,
266 // 'float3|999.1-10': 1,
267 // 'float4|.3-10': 123.123,
268 parts[0] = options.rule.range ? options.rule.count : parts[0]
269 parts[1] = (parts[1] || '').slice(0, options.rule.dcount)
270 for (i = 0; parts[1].length < options.rule.dcount; i++) {
271 parts[1] += Random.character('number')
272 }
273 result = parseFloat(parts.join('.'), 10)
274 } else { // integer
275 // 'grade1|1-100': 1,
276 result = options.rule.range && !options.rule.parameters[2] ? options.rule.count : options.template
277 }
278 return result
279 },
280 boolean: function(options) {
281 var result;
282 // 'prop|multiple': false, 当前值是相反值的概率倍数
283 // 'prop|probability-probability': false, 当前值与相反值的概率
284 result = options.rule.parameters ? Random.bool(options.rule.min, options.rule.max, options.template) : options.template
285 return result
286 },
287 string: function(options) {
288 var result = '',
289 i, placeholders, ph, phed;
290 if (options.template.length) {
291 // 'star|1-5': '★',
292 for (i = 0; i < options.rule.count; i++) {
293 result += options.template
294 }
295 // 'email|1-10': '@EMAIL, ',
296 placeholders = result.match(rplaceholder) || [] // A-Z_0-9 > \w_
297 for (i = 0; i < placeholders.length; i++) {
298 ph = placeholders[i]
299 // TODO 只有转义斜杠是偶数时,才不需要解析占位符?
300 if (/^\\/.test(ph)) {
301 placeholders.splice(i--, 1)
302 continue
303 }
304 phed = Handle.placeholder(ph, options.context.currentContext, options.context.templateCurrentContext)
305 // 只有一个占位符,并且没有其他字符
306 if (placeholders.length === 1 && ph === result && typeof phed !== typeof result) { //
307 result = phed
308 break
309
310 if (Util.isNumeric(phed)) {
311 result = parseFloat(phed, 10)
312 break
313 }
314 if (/^(true|false)$/.test(phed)) {
315 result = phed === 'true' ? true :
316 phed === 'false' ? false :
317 phed // 已经是布尔值
318 break
319 }
320 }
321 result = result.replace(ph, phed)
322 }
323 } else {
324 // 'ASCII|1-10': '',
325 // 'ASCII': '',
326 result = options.rule.range ? Random.string(options.rule.count) : options.template
327 }
328 return result
329 },
330 'function': function(options) {
331 return options.template.call(options.context.currentContext)
332 }
333})
334
335Handle.extend({
336 _all: function() {
337 var re = {};
338 for (var key in Random) re[key.toLowerCase()] = key
339 return re
340 },
341 placeholder: function(placeholder, obj, templateContext) {
342 // 1 key, 2 params
343 rplaceholder.exec('')
344 var parts = rplaceholder.exec(placeholder),
345 key = parts && parts[1],
346 lkey = key && key.toLowerCase(),
347 okey = this._all()[lkey],
348 params = parts && parts[2] || ''
349
350 // 解析占位符的参数
351 try {
352 // 1. 尝试保持参数的类型
353 /*
354 #24 [Window Firefox 30.0 引用 占位符 抛错](https://github.com/nuysoft/Mock/issues/24)
355 [BX9056: 各浏览器下 window.eval 方法的执行上下文存在差异](http://www.w3help.org/zh-cn/causes/BX9056)
356 应该属于 Window Firefox 30.0 的 BUG
357 */
358 /* jshint -W061 */
359 params = eval('(function(){ return [].splice.call(arguments, 0 ) })(' + params + ')')
360 } catch (error) {
361 // 2. 如果失败,只能解析为字符串
362 // console.error(error)
363 // if (error instanceof ReferenceError) params = parts[2].split(/,\s*/);
364 // else throw error
365 params = parts[2].split(/,\s*/)
366 }
367
368 // 占位符优先引用数据模板中的属性
369 if (obj && (key in obj)) return obj[key]
370
371 if (templateContext &&
372 (typeof templateContext === 'object') &&
373 (key in templateContext) &&
374 (placeholder !== templateContext[key]) // fix #15 避免自己依赖自己
375 ) {
376 templateContext[key] = Handle.gen(templateContext[key], key, {
377 currentContext: obj,
378 templateCurrentContext: templateContext
379 })
380 return templateContext[key]
381 }
382
383 if (!(key in Random) && !(lkey in Random) && !(okey in Random)) return placeholder
384
385 for (var i = 0; i < params.length; i++) {
386 rplaceholder.exec('')
387 if (rplaceholder.test(params[i])) {
388 params[i] = Handle.placeholder(params[i], obj)
389 }
390 }
391
392 var handle = Random[key] || Random[lkey] || Random[okey]
393 switch (Util.type(handle)) {
394 case 'array':
395 return Random.pick(handle)
396 case 'function':
397 var re = handle.apply(Random, params)
398 if (re === undefined) re = ''
399 return re
400 }
401 }
402})
403// END(BROWSER)
\No newline at end of file