cbb8f6dfc13bc55d2b9fd0ff1035e44d65b423c67a723d96254ad83af9de58cc2f5f98ddcfeeae6127f2fc0472185d75222f2b31a1c09d0838559091ccdc21 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. /*
  2. ## Handler
  3. 处理数据模板。
  4. * Handler.gen( template, name?, context? )
  5. 入口方法。
  6. * Data Template Definition, DTD
  7. 处理数据模板定义。
  8. * Handler.array( options )
  9. * Handler.object( options )
  10. * Handler.number( options )
  11. * Handler.boolean( options )
  12. * Handler.string( options )
  13. * Handler.function( options )
  14. * Handler.regexp( options )
  15. 处理路径(相对和绝对)。
  16. * Handler.getValueByKeyPath( key, options )
  17. * Data Placeholder Definition, DPD
  18. 处理数据占位符定义
  19. * Handler.placeholder( placeholder, context, templateContext, options )
  20. */
  21. var Constant = require('./constant')
  22. var Util = require('./util')
  23. var Parser = require('./parser')
  24. var Random = require('./random/')
  25. var RE = require('./regexp')
  26. var Handler = {
  27. extend: Util.extend
  28. }
  29. /*
  30. template 属性值(即数据模板)
  31. name 属性名
  32. context 数据上下文,生成后的数据
  33. templateContext 模板上下文,
  34. Handle.gen(template, name, options)
  35. context
  36. currentContext, templateCurrentContext,
  37. path, templatePath
  38. root, templateRoot
  39. */
  40. Handler.gen = function(template, name, context) {
  41. /* jshint -W041 */
  42. name = name == undefined ? '' : (name + '')
  43. context = context || {}
  44. context = {
  45. // 当前访问路径,只有属性名,不包括生成规则
  46. path: context.path || [Constant.GUID],
  47. templatePath: context.templatePath || [Constant.GUID++],
  48. // 最终属性值的上下文
  49. currentContext: context.currentContext,
  50. // 属性值模板的上下文
  51. templateCurrentContext: context.templateCurrentContext || template,
  52. // 最终值的根
  53. root: context.root || context.currentContext,
  54. // 模板的根
  55. templateRoot: context.templateRoot || context.templateCurrentContext || template
  56. }
  57. // console.log('path:', context.path.join('.'), template)
  58. var rule = Parser.parse(name)
  59. var type = Util.type(template)
  60. var data
  61. if (Handler[type]) {
  62. data = Handler[type]({
  63. // 属性值类型
  64. type: type,
  65. // 属性值模板
  66. template: template,
  67. // 属性名 + 生成规则
  68. name: name,
  69. // 属性名
  70. parsedName: name ? name.replace(Constant.RE_KEY, '$1') : name,
  71. // 解析后的生成规则
  72. rule: rule,
  73. // 相关上下文
  74. context: context
  75. })
  76. if (!context.root) context.root = data
  77. return data
  78. }
  79. return template
  80. }
  81. Handler.extend({
  82. array: function(options) {
  83. var result = [],
  84. i, ii;
  85. // 'name|1': []
  86. // 'name|count': []
  87. // 'name|min-max': []
  88. if (options.template.length === 0) return result
  89. // 'arr': [{ 'email': '@EMAIL' }, { 'email': '@EMAIL' }]
  90. if (!options.rule.parameters) {
  91. for (i = 0; i < options.template.length; i++) {
  92. options.context.path.push(i)
  93. options.context.templatePath.push(i)
  94. result.push(
  95. Handler.gen(options.template[i], i, {
  96. path: options.context.path,
  97. templatePath: options.context.templatePath,
  98. currentContext: result,
  99. templateCurrentContext: options.template,
  100. root: options.context.root || result,
  101. templateRoot: options.context.templateRoot || options.template
  102. })
  103. )
  104. options.context.path.pop()
  105. options.context.templatePath.pop()
  106. }
  107. } else {
  108. // 'method|1': ['GET', 'POST', 'HEAD', 'DELETE']
  109. if (options.rule.min === 1 && options.rule.max === undefined) {
  110. // fix #17
  111. options.context.path.push(options.name)
  112. options.context.templatePath.push(options.name)
  113. result = Random.pick(
  114. Handler.gen(options.template, undefined, {
  115. path: options.context.path,
  116. templatePath: options.context.templatePath,
  117. currentContext: result,
  118. templateCurrentContext: options.template,
  119. root: options.context.root || result,
  120. templateRoot: options.context.templateRoot || options.template
  121. })
  122. )
  123. options.context.path.pop()
  124. options.context.templatePath.pop()
  125. } else {
  126. // 'data|+1': [{}, {}]
  127. if (options.rule.parameters[2]) {
  128. options.template.__order_index = options.template.__order_index || 0
  129. options.context.path.push(options.name)
  130. options.context.templatePath.push(options.name)
  131. result = Handler.gen(options.template, undefined, {
  132. path: options.context.path,
  133. templatePath: options.context.templatePath,
  134. currentContext: result,
  135. templateCurrentContext: options.template,
  136. root: options.context.root || result,
  137. templateRoot: options.context.templateRoot || options.template
  138. })[
  139. options.template.__order_index % options.template.length
  140. ]
  141. options.template.__order_index += +options.rule.parameters[2]
  142. options.context.path.pop()
  143. options.context.templatePath.pop()
  144. } else {
  145. // 'data|1-10': [{}]
  146. for (i = 0; i < options.rule.count; i++) {
  147. // 'data|1-10': [{}, {}]
  148. for (ii = 0; ii < options.template.length; ii++) {
  149. options.context.path.push(result.length)
  150. options.context.templatePath.push(ii)
  151. result.push(
  152. Handler.gen(options.template[ii], result.length, {
  153. path: options.context.path,
  154. templatePath: options.context.templatePath,
  155. currentContext: result,
  156. templateCurrentContext: options.template,
  157. root: options.context.root || result,
  158. templateRoot: options.context.templateRoot || options.template
  159. })
  160. )
  161. options.context.path.pop()
  162. options.context.templatePath.pop()
  163. }
  164. }
  165. }
  166. }
  167. }
  168. return result
  169. },
  170. object: function(options) {
  171. var result = {},
  172. keys, fnKeys, key, parsedKey, inc, i;
  173. // 'obj|min-max': {}
  174. /* jshint -W041 */
  175. if (options.rule.min != undefined) {
  176. keys = Util.keys(options.template)
  177. keys = Random.shuffle(keys)
  178. keys = keys.slice(0, options.rule.count)
  179. for (i = 0; i < keys.length; i++) {
  180. key = keys[i]
  181. parsedKey = key.replace(Constant.RE_KEY, '$1')
  182. options.context.path.push(parsedKey)
  183. options.context.templatePath.push(key)
  184. result[parsedKey] = Handler.gen(options.template[key], key, {
  185. path: options.context.path,
  186. templatePath: options.context.templatePath,
  187. currentContext: result,
  188. templateCurrentContext: options.template,
  189. root: options.context.root || result,
  190. templateRoot: options.context.templateRoot || options.template
  191. })
  192. options.context.path.pop()
  193. options.context.templatePath.pop()
  194. }
  195. } else {
  196. // 'obj': {}
  197. keys = []
  198. fnKeys = [] // #25 改变了非函数属性的顺序,查找起来不方便
  199. for (key in options.template) {
  200. (typeof options.template[key] === 'function' ? fnKeys : keys).push(key)
  201. }
  202. keys = keys.concat(fnKeys)
  203. /*
  204. 会改变非函数属性的顺序
  205. keys = Util.keys(options.template)
  206. keys.sort(function(a, b) {
  207. var afn = typeof options.template[a] === 'function'
  208. var bfn = typeof options.template[b] === 'function'
  209. if (afn === bfn) return 0
  210. if (afn && !bfn) return 1
  211. if (!afn && bfn) return -1
  212. })
  213. */
  214. for (i = 0; i < keys.length; i++) {
  215. key = keys[i]
  216. parsedKey = key.replace(Constant.RE_KEY, '$1')
  217. options.context.path.push(parsedKey)
  218. options.context.templatePath.push(key)
  219. result[parsedKey] = Handler.gen(options.template[key], key, {
  220. path: options.context.path,
  221. templatePath: options.context.templatePath,
  222. currentContext: result,
  223. templateCurrentContext: options.template,
  224. root: options.context.root || result,
  225. templateRoot: options.context.templateRoot || options.template
  226. })
  227. options.context.path.pop()
  228. options.context.templatePath.pop()
  229. // 'id|+1': 1
  230. inc = key.match(Constant.RE_KEY)
  231. if (inc && inc[2] && Util.type(options.template[key]) === 'number') {
  232. options.template[key] += parseInt(inc[2], 10)
  233. }
  234. }
  235. }
  236. return result
  237. },
  238. number: function(options) {
  239. var result, parts;
  240. if (options.rule.decimal) { // float
  241. options.template += ''
  242. parts = options.template.split('.')
  243. // 'float1|.1-10': 10,
  244. // 'float2|1-100.1-10': 1,
  245. // 'float3|999.1-10': 1,
  246. // 'float4|.3-10': 123.123,
  247. parts[0] = options.rule.range ? options.rule.count : parts[0]
  248. parts[1] = (parts[1] || '').slice(0, options.rule.dcount)
  249. while (parts[1].length < options.rule.dcount) {
  250. parts[1] += (
  251. // 最后一位不能为 0:如果最后一位为 0,会被 JS 引擎忽略掉。
  252. (parts[1].length < options.rule.dcount - 1) ? Random.character('number') : Random.character('123456789')
  253. )
  254. }
  255. result = parseFloat(parts.join('.'), 10)
  256. } else { // integer
  257. // 'grade1|1-100': 1,
  258. result = options.rule.range && !options.rule.parameters[2] ? options.rule.count : options.template
  259. }
  260. return result
  261. },
  262. boolean: function(options) {
  263. var result;
  264. // 'prop|multiple': false, 当前值是相反值的概率倍数
  265. // 'prop|probability-probability': false, 当前值与相反值的概率
  266. result = options.rule.parameters ? Random.bool(options.rule.min, options.rule.max, options.template) : options.template
  267. return result
  268. },
  269. string: function(options) {
  270. var result = '',
  271. i, placeholders, ph, phed;
  272. if (options.template.length) {
  273. // 'foo': '★',
  274. /* jshint -W041 */
  275. if (options.rule.count == undefined) {
  276. result += options.template
  277. }
  278. // 'star|1-5': '★',
  279. for (i = 0; i < options.rule.count; i++) {
  280. result += options.template
  281. }
  282. // 'email|1-10': '@EMAIL, ',
  283. placeholders = result.match(Constant.RE_PLACEHOLDER) || [] // A-Z_0-9 > \w_
  284. for (i = 0; i < placeholders.length; i++) {
  285. ph = placeholders[i]
  286. // 遇到转义斜杠,不需要解析占位符
  287. if (/^\\/.test(ph)) {
  288. placeholders.splice(i--, 1)
  289. continue
  290. }
  291. phed = Handler.placeholder(ph, options.context.currentContext, options.context.templateCurrentContext, options)
  292. // 只有一个占位符,并且没有其他字符
  293. if (placeholders.length === 1 && ph === result && typeof phed !== typeof result) { //
  294. result = phed
  295. break
  296. if (Util.isNumeric(phed)) {
  297. result = parseFloat(phed, 10)
  298. break
  299. }
  300. if (/^(true|false)$/.test(phed)) {
  301. result = phed === 'true' ? true :
  302. phed === 'false' ? false :
  303. phed // 已经是布尔值
  304. break
  305. }
  306. }
  307. result = result.replace(ph, phed)
  308. }
  309. } else {
  310. // 'ASCII|1-10': '',
  311. // 'ASCII': '',
  312. result = options.rule.range ? Random.string(options.rule.count) : options.template
  313. }
  314. return result
  315. },
  316. 'function': function(options) {
  317. // ( context, options )
  318. return options.template.call(options.context.currentContext, options)
  319. },
  320. 'regexp': function(options) {
  321. var source = ''
  322. // 'name': /regexp/,
  323. /* jshint -W041 */
  324. if (options.rule.count == undefined) {
  325. source += options.template.source // regexp.source
  326. }
  327. // 'name|1-5': /regexp/,
  328. for (var i = 0; i < options.rule.count; i++) {
  329. source += options.template.source
  330. }
  331. return RE.Handler.gen(
  332. RE.Parser.parse(
  333. source
  334. )
  335. )
  336. }
  337. })
  338. Handler.extend({
  339. _all: function() {
  340. var re = {};
  341. for (var key in Random) re[key.toLowerCase()] = key
  342. return re
  343. },
  344. // 处理占位符,转换为最终值
  345. placeholder: function(placeholder, obj, templateContext, options) {
  346. // console.log(options.context.path)
  347. // 1 key, 2 params
  348. Constant.RE_PLACEHOLDER.exec('')
  349. var parts = Constant.RE_PLACEHOLDER.exec(placeholder),
  350. key = parts && parts[1],
  351. lkey = key && key.toLowerCase(),
  352. okey = this._all()[lkey],
  353. params = parts && parts[2] || ''
  354. var pathParts = this.splitPathToArray(key)
  355. // 解析占位符的参数
  356. try {
  357. // 1. 尝试保持参数的类型
  358. /*
  359. #24 [Window Firefox 30.0 引用 占位符 抛错](https://github.com/nuysoft/Mock/issues/24)
  360. [BX9056: 各浏览器下 window.eval 方法的执行上下文存在差异](http://www.w3help.org/zh-cn/causes/BX9056)
  361. 应该属于 Window Firefox 30.0 的 BUG
  362. */
  363. /* jshint -W061 */
  364. params = eval('(function(){ return [].splice.call(arguments, 0 ) })(' + params + ')')
  365. } catch (error) {
  366. // 2. 如果失败,只能解析为字符串
  367. // console.error(error)
  368. // if (error instanceof ReferenceError) params = parts[2].split(/,\s*/);
  369. // else throw error
  370. params = parts[2].split(/,\s*/)
  371. }
  372. // 占位符优先引用数据模板中的属性
  373. if (obj && (key in obj)) return obj[key]
  374. // @index @key
  375. // if (Constant.RE_INDEX.test(key)) return +options.name
  376. // if (Constant.RE_KEY.test(key)) return options.name
  377. // 绝对路径 or 相对路径
  378. if (
  379. key.charAt(0) === '/' ||
  380. pathParts.length > 1
  381. ) return this.getValueByKeyPath(key, options)
  382. // 递归引用数据模板中的属性
  383. if (templateContext &&
  384. (typeof templateContext === 'object') &&
  385. (key in templateContext) &&
  386. (placeholder !== templateContext[key]) // fix #15 避免自己依赖自己
  387. ) {
  388. // 先计算被引用的属性值
  389. templateContext[key] = Handler.gen(templateContext[key], key, {
  390. currentContext: obj,
  391. templateCurrentContext: templateContext
  392. })
  393. return templateContext[key]
  394. }
  395. // 如果未找到,则原样返回
  396. if (!(key in Random) && !(lkey in Random) && !(okey in Random)) return placeholder
  397. // 递归解析参数中的占位符
  398. for (var i = 0; i < params.length; i++) {
  399. Constant.RE_PLACEHOLDER.exec('')
  400. if (Constant.RE_PLACEHOLDER.test(params[i])) {
  401. params[i] = Handler.placeholder(params[i], obj, templateContext, options)
  402. }
  403. }
  404. var handle = Random[key] || Random[lkey] || Random[okey]
  405. switch (Util.type(handle)) {
  406. case 'array':
  407. // 自动从数组中取一个,例如 @areas
  408. return Random.pick(handle)
  409. case 'function':
  410. // 执行占位符方法(大多数情况)
  411. handle.options = options
  412. var re = handle.apply(Random, params)
  413. if (re === undefined) re = '' // 因为是在字符串中,所以默认为空字符串。
  414. delete handle.options
  415. return re
  416. }
  417. },
  418. getValueByKeyPath: function(key, options) {
  419. var originalKey = key
  420. var keyPathParts = this.splitPathToArray(key)
  421. var absolutePathParts = []
  422. // 绝对路径
  423. if (key.charAt(0) === '/') {
  424. absolutePathParts = [options.context.path[0]].concat(
  425. this.normalizePath(keyPathParts)
  426. )
  427. } else {
  428. // 相对路径
  429. if (keyPathParts.length > 1) {
  430. absolutePathParts = options.context.path.slice(0)
  431. absolutePathParts.pop()
  432. absolutePathParts = this.normalizePath(
  433. absolutePathParts.concat(keyPathParts)
  434. )
  435. }
  436. }
  437. try {
  438. key = keyPathParts[keyPathParts.length - 1]
  439. var currentContext = options.context.root
  440. var templateCurrentContext = options.context.templateRoot
  441. for (var i = 1; i < absolutePathParts.length - 1; i++) {
  442. currentContext = currentContext[absolutePathParts[i]]
  443. templateCurrentContext = templateCurrentContext[absolutePathParts[i]]
  444. }
  445. // 引用的值已经计算好
  446. if (currentContext && (key in currentContext)) return currentContext[key]
  447. // 尚未计算,递归引用数据模板中的属性
  448. if (templateCurrentContext &&
  449. (typeof templateCurrentContext === 'object') &&
  450. (key in templateCurrentContext) &&
  451. (originalKey !== templateCurrentContext[key]) // fix #15 避免自己依赖自己
  452. ) {
  453. // 先计算被引用的属性值
  454. templateCurrentContext[key] = Handler.gen(templateCurrentContext[key], key, {
  455. currentContext: currentContext,
  456. templateCurrentContext: templateCurrentContext
  457. })
  458. return templateCurrentContext[key]
  459. }
  460. } catch(err) { }
  461. return '@' + keyPathParts.join('/')
  462. },
  463. // https://github.com/kissyteam/kissy/blob/master/src/path/src/path.js
  464. normalizePath: function(pathParts) {
  465. var newPathParts = []
  466. for (var i = 0; i < pathParts.length; i++) {
  467. switch (pathParts[i]) {
  468. case '..':
  469. newPathParts.pop()
  470. break
  471. case '.':
  472. break
  473. default:
  474. newPathParts.push(pathParts[i])
  475. }
  476. }
  477. return newPathParts
  478. },
  479. splitPathToArray: function(path) {
  480. var parts = path.split(/\/+/);
  481. if (!parts[parts.length - 1]) parts = parts.slice(0, -1)
  482. if (!parts[0]) parts = parts.slice(1)
  483. return parts;
  484. }
  485. })
  486. module.exports = Handler