Such.define
>= 1.0.0
Such.define
It is the entry method to difine a custom type, including a configuration data called by API Such.config
, are called it to add data types. It accepts four kinds of parameters, corresponding to different situations.
The first: Based on existing types, a new type is obtained by fixing certain data attributes. Most of the built-in extended types in library are defined in this way.
Such.define(newType: string, baseType: string, attributes: string)
// example Such.define("integer", "number", "%d");
1
2The second: Define a new type by providing the
generate
function to generate data. This way is more suitable for types that do not need to provide any data attribute parameters.Such.define(newType: string, generate: (options: TSuchInject) => unkown)
// Extended boolean type Such.define("boolean", function (_, such) { // The second parameter provides the Such class, // which is convenient to use the utils method mounted on it // It is also convenient to combine other data through other simulation data return such.utils.isOptional(); }); // Extend an rgb color type Such.define("color$rgb", function (_, such) { // Because the value of rgb is random between 0 and 255 // So you can define a random instance between 0 and 255 const instance = such.instance(":int[0,255]"); return "rgb(" + [instance.a(), instance.a(), instance.a()].join(",") + ")"; });
1
2
3
4
5
6
7
8
9
10
11
12
13
14The third: Create a new type by providing complete configuration parameters, which is close to the definition how the library define built-in basic types.
Such.define(newType: string, config: TMFactoryOptions)
The definition of
TMFactoryOptions
is as follows:param
optional, corresponding to theattributes
data attribute in the first form, which is equivalent to providing the defaultdata attribute
s.init(utils: typeof Such.utils)
optional, the method to be executed during initialization, is mainly fordata attribute
parsing and formatting. In order to ensure the scope ofthis
, please do not use arrow functions. The parameterutils
are injected bySuch.utils
.genreate(options?: TSuchInject, such?: Such)
required, the method to be finally executed to generate mocking data. This method accepts an options parameter and Such class injection. The definition of optionsTSuchInject
contains the following parameters:datas
The entire mocking data that has been generated when themocker
runs to the current field, the data is gradually generated in a depth-first manner.mocker
The mocker object instance used by the current field, which has aparent
mocker object and aroot
mocker object, organized in a tree-like structure. At the same time, there is astoreData
field on the object, which can be used to store some data that needs to be saved on themocker
object. For example, the:increment
type will save the id value generated last time, so that the data value can be maintained during the next generation, keep the data in updated.dpath
The current path value of the data field to be generated, similar to xml'sxpath
.
reGenerate
optional, this parameter will be ignored in this form, and the definition of next, the fourth form, can be used to override thegenerate
method of the original type.configOptions
optional, corresponding to the configurationdata attribute
of#[]
, which can be used to set the default value and data type of certain parameters, similar to thevue
how to declares the accepted property parameters in the child components.Such.define("datetime", { param: "%yyyy-mm-dd HH\\:MM\\:ss", init(utils) { // range this.addRule("$size", function ($size) { if (!$size) { return; } const { range } = $size; const makeDate = (param) => { let date; if (!isNaN(param)) { date = new Date(param); } else if (strRule.test(param)) { date = utils.strtotime(RegExp.$2); } else { throw new Error(`invalid date:${param}`); } return date; }; if (range.length !== 2) { throw new Error( `the time range should supply 2 arguments,but got ${range.length}` ); } else { const [start, end] = range; const startdate = makeDate(start); const enddate = makeDate(end); const starttime = startdate.getTime(); const endtime = enddate.getTime(); if (endtime < starttime) { throw new Error( `the time range of start time ${start} is big than end time ${end}.` ); } else { return { range: [starttime, endtime], }; } } }); // $format rule this.addRule("$format", function ($format) { // nothing let { format } = $format; format = utils.decodeTrans(format.slice(1)); return { format, }; }); // modifier this.addModifier("$format", function (result, $format) { const { format } = $format; return utils.dateformat(format, result); }); }, generate(_, such) { const { $size } = this.params; const { utils } = such; const range = ( $size ?? { range: [ utils.strtotime("-10 year").getTime(), utils.strtotime("+10 year").getTime(), ], } ).range; const time = utils.makeRandom(range[0], range[1]); return new Date(time); }, });
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
The fourth: similar to the third form, but an additional inheritance type
baseType
parameter is added. This form is currently less used.
Such.parser
>= 1.0.0
The parser
in Suchjs is for the analysis of data attribute
s. The built-in parser
s include:
[min,max]
is used to parse the size range, such as[1,100]
{least[,most]}
is used to parse the length, such as{3}
,{3,5}
%
is used to parse and format, followed by theformat
string, such as%.2f
for number type and%yyyy-mm-dd
for date type/
is used to parse the regular expression, followed by thepattern
string, such as the regular expression/\w/
&
is used to parse the path, multiple paths can separate by the comma,
, such as&./firstName,./lastName
of the:ref
type, or file path of dictionary type&<dataDir>/dict.txt
@
is used to parse function calls, and multiple functions separate by the vertical bar|
as a pipe, such as@repeat(3)|join('')
#[key=value]
is used to parse the configuration, also can use a comma,
to separate multiple key-value pairs, such as a:increment
type can set a configuration like#[start=0,step=2]
For general data types, these basic data attribute
s are sufficient, but if you meet some needs can't cover them by those, you may need to add a new data attribute
parser through the API Such.parser
.
Such.parser(name: string, params: {config: IParserConfig, parse: () => void, setting: TObj})
The description of each parameter is as follows:
name
defines the name of the parser, it also used for the parsed result's key.params
the main parameter to define how the parser parsing data.config
is a typescript typeIParserConfig
, which is defined as:startTag
string array type, representing the start tag ofparser
endTag
string array type, representing the end tag ofparser
separator
If the data supports grouping, the separator between multiple groups, note that the separator can't be the same as the separator:
that has ever used by thedata attribute
spattern
a regular expression, for grouping, if a simpleseparator
parameter can't simply separate the groups, you can set apattern
to separate the groupsrule
a regular expression, if you can't simply use the start and end tags to match the entire data attribute value, you can set therule
to match the whole.
parse: () => void
After the parsed string data is obtained through the above configuration, theparse
method is further parsed into usable data, because in theparse
method, it will need to be used The general method of the inherited parent classParser
, so theparse
method should not use arrow functions to ensure the correct point ofthis
.setting
a configuration object. Currently, just a boolean fieldfrozen
is provided. if thefrozen
is true, then thedata attribute
can't be set repeatedly, otherwise thedata attribute
can be set twice more, such as the configuration attribute,#[a=1]:#[b=1]
, the final data will be merged the same to#[a=1,b=1]
.
Now add a parser
according to the above method, the code is as follows:
// Define a parser to parse the form `(1,2,"hello,world",3,'good job!')`
// This format starts with a left parenthesis `(` and ends with a right parenthesis `)`
// Separate by comma `,`
// Because there may be English commas `,` in the string, you need to set the pattern to do complex matching
Such.parser("numberAndString", {
config: {
startTag: ["("],
endTag: [")"],
separator: ",",
pattern: /^\s*(?:(['"])(?:(?!\1)[^\\]|\\.)*\1|\d+)/,
},
parse() {
const { patterns } = this;
const data = [];
patterns.map((match) => {
let [value, quote] = match;
value = value.trimStart();
if (quote) {
// is a string
data.push(value);
} else {
// is a number
data.push(Number(value));
}
});
return data;
},
setting: {
frozen: true,
},
});
// After defining `parser`, let's define a custom type and parse the data of the `parser`.
Such.define("showdata", {
init() {
// Define further processing of the numberAndString parsed by the parser
// The first parameter name corresponding to addRule is consistent with the defined parser name
this.addRule("numberAndString", function (numAndStr) {
if (!numAndStr) {
return;
}
const numbers = [];
const strings = [];
numAndStr.map((val) => {
if (typeof val === "string") {
strings.push(val);
} else {
numbers.push(val);
}
});
return {
numbers,
strings,
};
});
},
generate(_, such) {
// The final data will be parsed into params, the key is the name of the parser
const { numberAndString } = this.params;
const { numbers, strings } = numberAndString;
let ret = [];
const totalNum = numbers.length;
if (totalNum) {
const index = such.utils.makeRandom(0, totalNum - 1);
ret.push(`number:${numbers[index]}`);
}
const totalStr = strings.length;
if (totalStr) {
const index = such.utils.makeRandom(0, totalStr - 1);
ret.push(`string:${strings[index]}`);
}
return ret.join("|");
},
});
// Now you can use custom types to simulate data
Such.as(':showdata:(1, 2, "hello", 3, 4, "world")');
// Will output `number:1|string:world`, `number:4|string:hello` etc.
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
Such.alias
>= 1.0.0
Define type alias, it's just need provide two arguments, one is the abbreviated alias name, the other is the actual existing long type name.
Such.alias(alias: string, fromType: string)
// Add an alias int for the integer type
Such.alias("int", "integer");
2
Such.config
>= 1.0.0
With the above three methods, we can easily extend the types supported by Suchjs. In order to quickly define these data all, Suchjs provides this method:
Such.config(settings: TSuchSettings)
What the settings
look like is shown in the below code.
Such.config({
// Here the corresponding call will be made using the `Such.parser` method
parsers: {
// key is the name of the corresponding parser, and the value is the second parsing configuration parameter
numberAndString: {
config: {
// ...The code can refer to the chapter defining the parser above
},
},
},
// Correspondingly, the `Such.define` method will be used to call
// where key is the name of the defined type
// For the array value value, the parameter will be expanded in the apply way
types: {
integer: ["number", "%d"],
boolean: function (_, such) {
return such.utils.isOptional();
},
},
// This will call `Such.alias` to create an alias
// key is the alias name, value is the original type name
alias: {
int: "integer",
bool: "boolean",
},
// The following configuration is only used in the Node environment
// 'extends' it's an array list of config file should loaded first.
// The config file use the same format as the current config parameter
// It can be a json file or a CMD module
// The ones beginning with 'such:' are built-in config modules
extends: ["such:recommend"],
// The file paths & preload configuration
config: {
// Whether to preload all data files, this is mainly for ':dict' data type and ':cascade' data type
// Can be boolean, true means preload all files
// It can also be an array, specifying the data file need to be preloaded
preload: false,
// In the node environment, Such.as('*.json') will get the json file directly from this directory
// Generate simulation data with the json file as the data configuration
suchDir: "suchas",
// The path to store the data file
dataDir: "suchas/data",
},
});
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
Such.assign
>= 1.0.0
As mentioned earlier, all of our data mocking support function calls data attribute
starting with @
and data configuration data attribute
like #[key=value]
, so if you want to inject your own function call names and data configuration's value data variable, you need to use Such.assign
.
Such.assign(key: string, value: unkown)
// Define a string truncation method
Such.assign("truncate", function (str, len) {
if (str.length > len) {
return str.slice(0, len) + "...";
}
return str;
});
// use it
Such.as(":string:{20}:@truncate(10)");
// Output is similar to:'tALIHe(|ff...'
2
3
4
5
6
7
8
9
10
Such.instance
>= 1.0.0
It's a static method to directly generate a Such
object instance instead of new Such
, it is recommended. If your data needs to be generated multiple times, you need use it to create an instance; the static method Such.as(target, options?)
is a syntactic sugar method just like this: Such.instance(target, options?).a()
.
const IDGenerator = Such.instance(":increment");
// Generate a simulation data
IDGenerator.a(); // 1
IDGenerator.a(); // 2
2
3
4
Since the version v1.2.0
, the optional parameter of instanceOptions
is supported when calling the a(instanceOptions?: IAInstanceOptions)
method. This parameter currently supports the configuration of the keys
field, it's an object use the path of the data as the key
, use an object {min?: number, max?: number}
as the value
.It's used to set different configurations for each simulation data. At present, it mainly focuses on optional fields and array fields.
// the declaration of the type IAInstanceOptions
type IAInstanceOptions = {
keys?: {
[key: string]: {
min?: number;
max?: number;
exist?: boolean;
index?: number;
};
};
};
// The `key` is a path of the data, specify the number of occurrences of the field which the path pointed to.
2
3
4
5
6
7
8
9
10
11
12
// Example of configuring optional fields
const genOptional = Such.instance({
"optional?": ":boolean",
});
// Without parameters, the `optional` field above may or may not exist
genOptional.a();
// Add `instanceOptions` parameter
// The `optional` field of the following calls will never exist
genOptional.a({
keys: {
"/optional": {
// `max` is 0, which means that the number of occurrences of the `optional` field can only be 0
// This is equivalent to "exist: false"
// If the field is both optional and has an array length, you should just use "exist"
max: 0,
},
},
});
// The `optional` field of the following calls will always exist
genOptional.a({
keys: {
"/optional": {
// `min` is 1, indicating that the number of occurrences of the optional field can only be 1
// This is equivalent to "exist: true"
// If the field is both optional and has an array length, you should just use "exist"
min: 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
// Example of configuration array field
const genArray = Such.instance({
"array{5,10}": ":number",
});
// Without parameters, so the length of the array is 5 to 10
genArray.a();
// Add `instanceOptions` parameter
// The following parameters will limit the length of the array to 6 to 8
// Note that the range of this parameter can only be narrowed within the original range, not widened
genOptional.a({
keys: {
"/array": {
min: 6,
max: 8,
},
},
});
// The following call will determine the length of the array to 6
genOptional.a({
keys: {
"/array": {
min: 6,
max: 6,
},
},
});
// For enumeration types, you can also specify the index value of the enumeration
const genResult = Such.instance({
"errno{1}": [0, 1],
});
genResult.a({
keys: {
"/errno": {
index: 0,
},
},
});
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
Such.as
>= 1.0.0
As mentioned in the above Such.instance
static method, this method provides a quick method entry that only requires generate a simulation data once.
Such.template
>= 1.1.0
This method is also the method actually called by the template literal type. The difference is that it does not require the leading three colons :::
as the type identifier. It accepts a string template, if it is a data type variable, it can be wrapped with the (backtick => "`") symbol. If there are other data attribute parameters, the three colons :::
are still used to indicate that the template literal is end, and the following string will be parsed as the type of data attribute
s. The data attribute
s supported by the current version only includes length data attribute
like {3}
, indicating how many times the previous template string is repeated. If you need to output the normal backtick ` symbol, or the three colons :::
, please add a backslash \\
in front of it to escape.
Such.template(key: string, path?: TFieldPath)
// When only call itself, no need to provide the path parameter like a xpath of data
// The path parameter is mainly provided when using the template string type to facilitate better prompts for errors
Such.assign("dict", ["bear", "rabbit"]);
// return a Template object
const tmpl = Such.template(
"i spent `:number:[50,100]` dollars to buy a `:dict:#[data=dict]` toy."
);
// like the `Such` instance, it has a `a()` method to generate a value base on the template instance
tmpl.a();
const ucaseTmpl = Such.template("`:uppercase:{3}`:::{3}");
ucaseTmpl.a(); // Output similar to: "ACDACDACD"
2
3
4
5
6
7
8
9
10
11
The above is basically the main APIs provided by Suchjs
, and other APIs may be added and changed as the library's version changes. If you have any good comments, please feel free to provide feedback in github
.
There are also some APIs based on data caching, loading and updating in the Nodejs environment, which will be explained in a separate chapter.