Skip to main content

One value at a time

Sometimes you don't want a dataset. You want a surname, here, on this line of a test — the job a faker library does. TDC answers that from the same data packs its configs draw on, so the name in your unit test and the name in your million-row fixture come from one list.

All five implementations have it, and the same seed gives the same value in each:

import { tdc } from 'tdcv2';

tdc.person.lastName(); // Jones

That is the whole API. Everything below is that one call with something in front of it.

Every value shown on this page was drawn under the seed demo, so you can reproduce it. Without a seed each call is fresh; Making it repeat is where the seed goes.

This is the loose-values drawer

Every call is independent. Nothing here ties one value to another — no parent=, no <switch> on a drawn column, no uniq, no <compute>. A coherent record is a config; see Your first dataset. Use this when the values genuinely don't need to agree with each other.

One rule: a dot is a dot

person.male.firstName in your code is person.male.firstName in a config and in the reference. There is no second vocabulary to learn.

tdc.person.lastName(); // Jones
tdc.person.male.firstName(); // Robert
tdc.person.female.firstName(); // Linda
tdc.company.industry(); // Pharmaceuticals
tdc.color.name(); // Emerald
tdc.food.dish(); // Chicken Tikka Masala

The segments are spelled the way the packs spell them, camelCase and all, in Python and C# as much as in TypeScript. They are not names the library chose; renaming them per language would be a second vocabulary to keep in step with the reference, with a config, and with four other implementations.

A bare address is read against the active locale, exactly as in a config. In en you get Jones; switch the locale and the same line gives you a Russian surname.

Two spellings, one address

TypeScript, Python and C# walk the address as members — tdc.person.lastName() — because each of those languages can answer for a member that does not exist until it is asked for. Java and Rust take the address as a string instead.

That is a decision, not a gap. The member shape needs one generated method per address, and a generated surface can only cover the packs inside the artifact. Most packs are downloaded at run time, so a generated tdc.lang().ru() would not exist for a pack that was installed a minute ago, while get("ru.person.lastName") works the moment the download finishes.

Naming a pack outright

An address can reach past the active locale and name a pack. Java and Rust write that address as it is. TypeScript, Python and C# put common, country or lang in front of it — those three words carry no meaning inside an address, and they are there so the completion list at tdc. stays a list of categories rather than a wall of 122 pack codes.

ReachesTypeScript, Python, C#Java, Rust
the active localetdc.person.lastName()"person.lastName"
the shared pack — the same in every languagetdc.common.id.uuid()"common.id.uuid"
one country's packtdc.country.usa.docs.ssn()"usa.docs.ssn"
one language's packtdc.lang.ru.person.lastName()"ru.person.lastName"
tdc.common.id.uuid(); // 3ff6ff76-6ea7-4fad-8b99-3075a14cc7e9
tdc.common.internet.email(); // u99o89qpeo@test-qu8y3h.invalid
tdc.common.finance.iban(); // DE62299399441396459682
tdc.common.finance.currency(); // Swedish Krona

tdc.country.usa.docs.ssn(); // 699209702
tdc.country.usa.finance.aba_routing(); // 659939946

Those two identifiers are not shaped like one — they carry real check digits, the same ones a config would produce.

An address that isn't installed says so

common, en and the USA pack ship inside all five artifacts. Anything else is a download away, and asking for it before it is there gets you a named failure rather than a blank:

tdc.lang.ru.person.lastName();
// TdcQuickError: the "ru" pack is not installed, so "ru.person.lastName" cannot be
// drawn. Install it with `tdcv2 pack add ru` (run `tdcv2 init` once first, to say
// where packs go).

Only Java's wording differs, and only because Maven puts nothing on the PATH: advice to run tdcv2 would be advice a Java reader cannot type. The command line itself is the same in all five. See Installing packs.

A misspelled segment is a different failure, and says so: person.lastNam comes back as unknown address "person.lastNam" (locale "en"). Did you mean "en.person.lastName"?

Many at once

Ask for n values in one call instead of calling in a loop — it is one draw of n values, not n draws of one.

tdc.person.lastName.many(5);
// [ 'Jones', 'Bush', 'Armstrong', 'Andrews', 'Jimenez' ]

Making it repeat

By default every call is fresh — that is what you want in a scratch script. Pin a seed and the values become part of the test rather than a variable in it. Pinning a seed also returns a new object rather than changing the one you called it on, so two tests can hold different seeds at the same time.

const t = tdc.seed('demo');
t.person.lastName(); // Jones, today and next year

const ru = tdc.seed('fixtures').locale('ru');
const en = tdc.seed('fixtures').locale('en');
ru.person.lastName(); // Ткаченко
en.person.lastName(); // Pearson

Generators without a pack

The engine's own generators are reachable too, for the values that come from a rule rather than from a list. They take attributes instead of an address, so they live under one name of their own — pack categories are already called date, text and word, which leaves the top level occupied.

tdc.gen.number('18..80'); // 66
tdc.gen.regex('[A-Z]{2}-[0-9]{4}'); // FZ-3994

Every generator and its attributes are in the generators reference.

Values are always strings

Including numbers and dates. The engine's world is text — that is what lets one config produce CSV, SQL and JSON without changing — and a return type that varied with the address would be a different contract in each of the five. Convert at the call site when you need a number:

const age = Number(tdc.gen.number('18..80'));

When to use a config instead

Reach for a config the moment two values have to agree: a city that belongs to its country, an order total that matches its lines, a 30% share that has to be exactly 30%. That is what the rest of this documentation is about, and it starts at Your first dataset.

See also