Compare commits
9 Commits
e09e92ff03
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 976d2e5ac1 | |||
| 343c9fa98a | |||
| f206a18027 | |||
| 3dc1cf82f1 | |||
| 0d627d2b11 | |||
| ce8ea550a3 | |||
| 1035f8bb2a | |||
| ce59653ccd | |||
| ea9c1ad436 |
@@ -11,10 +11,17 @@
|
||||
- Gitea queues Actions runs that `/repos/{owner}/{repo}/actions/tasks` does not
|
||||
list, so an empty task list says nothing about whether CI ran; read
|
||||
`/repos/{owner}/{repo}/commits/<sha>/status`.
|
||||
- README goal 2 is gated at 7.0 on the `comprehension-panel` skill, and below it
|
||||
nothing else merges — no feature, no category, no data — bar a security fix and a
|
||||
dependency bump. `todo.md` carries the round: the nine-seat findings run at depth 1,
|
||||
a PR per item it names, then the run again, until the score passes. At or above 7.0
|
||||
every pull request scores with the four-seat run and answers it in one run. No merge
|
||||
lowers the last score.
|
||||
- Hard tabs. No comment by default; delete a restatement, a rationale, history, or a file preamble.
|
||||
- One spelling per result: reject the other at `New`, and let the error name the spelling to use.
|
||||
- A standing choice a reader would relitigate goes under Decisions in the README, not in a comment.
|
||||
- Prose naming a source states what that source states: read the register's own field
|
||||
before paraphrasing it.
|
||||
before paraphrasing it, and name the register you read before claiming none
|
||||
publishes a spelling.
|
||||
- A README example is a `json` block that loads and renders as a category; `readme_test.go` runs every one.
|
||||
- Cyclomatic complexity is gated at 14: the table-shaped dispatches (`linkParent`, `compileTemplate`, `renderEdges`) sit at it and stay whole; a function that would pass it is decomposed.
|
||||
|
||||
+2
-2
@@ -14,12 +14,12 @@ Every shipped dataset, its source, its licence and the attribution it asks for.
|
||||
| `sv_SE/first-name.tsv`, `last-name.tsv` | [SCB](https://www.scb.se/) names with at least two bearers, 31 December 2022 | CC0 1.0 | "Källa: SCB" | `data-import/names-se.py` |
|
||||
| `en_US/first-name.tsv` | [SSA](https://www.ssa.gov/oact/babynames/) baby names, births 1930 to 2020, through [hackerb9/ssa-baby-names](https://github.com/hackerb9/ssa-baby-names) | public domain | none required | `data-import/names-us.py` |
|
||||
| `en_US/last-name.tsv` | Census Bureau surnames occurring 100 or more times, 2010 | public domain | none required | `data-import/names-us.py` |
|
||||
| `sv_SE/sex.tsv`, `sv_SE/birth-number.tsv`, `sv_SE/title.tsv`, `en_US/sex.tsv`, `en_US/title.tsv`, `misc/car.tsv` | curated; Skatteverket's test birth numbers are facts, and `car.tsv` waits for an international source (goal 10) | — | — | — |
|
||||
| `sv_SE/sex.tsv`, `sv_SE/birth-number.tsv`, `sv_SE/title.tsv`, `en_US/sex.tsv`, `en_US/title.tsv`, `misc/car.tsv` | curated; Skatteverket's test birth numbers are facts, and `car.tsv` waits for an international source (goal 12) | — | — | — |
|
||||
| `misc/currency.tsv` | [datasets/currency-codes](https://github.com/datasets/currency-codes); symbols from [Unicode CLDR](https://github.com/unicode-org/cldr) `en.xml` and `root.xml` | PDDL 1.0; [Unicode License v3](https://www.unicode.org/license.txt) | CLDR: "Copyright © 1991-2025 Unicode, Inc. Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the United States and other countries." | `data-import/currency.py` |
|
||||
| `misc/httpmethod.tsv` | [IANA HTTP Method Registry](https://www.iana.org/assignments/http-methods/) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/httpmethod.py` |
|
||||
| `misc/httpstatus.tsv` | [IANA HTTP Status Code Registry](https://www.iana.org/assignments/http-status-codes/) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/httpstatus.py` |
|
||||
| `misc/language.tsv` | [datasets/language-codes](https://github.com/datasets/language-codes), the [Library of Congress](https://www.loc.gov/standards/iso639-2/) ISO 639-2 register | [PDDL 1.0](https://opendatacommons.org/licenses/pddl/1-0/) | none required | `data-import/language.py` |
|
||||
| `misc/loglevel.tsv` | [RFC 5424](https://www.rfc-editor.org/rfc/rfc5424.txt) Table 2, the syslog message severities; keywords from [POSIX](https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/syslog.h.html) `<syslog.h>` | facts | none required | `data-import/loglevel.py` |
|
||||
| `misc/loglevel.tsv` | [RFC 5424](https://www.rfc-editor.org/rfc/rfc5424.txt) Table 2, the syslog message severities; keywords from the [POSIX](https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/syslog.h.html) `<syslog.h>` severity macros | facts | none required | `data-import/loglevel.py` |
|
||||
| `misc/mimetype.tsv` | [mime-db](https://github.com/jshttp/mime-db), the IANA media type registry with filename extensions | [MIT](https://github.com/jshttp/mime-db/blob/master/LICENSE) | "Copyright (c) 2014 Jonathan Ong, Copyright (c) 2015-2022 Douglas Christopher Wilson" | `data-import/mimetype.py` |
|
||||
| `misc/port.tsv` | [IANA Service Name and Transport Protocol Port Number Registry](https://www.iana.org/assignments/service-names-port-numbers/) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/port.py` |
|
||||
| `misc/protocol.tsv` | [IANA Protocol Numbers](https://www.iana.org/assignments/protocol-numbers/) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/protocol.py` |
|
||||
|
||||
@@ -194,7 +194,8 @@ a fixture needs one a reader recognises.
|
||||
`misc.loglevel` is the eight syslog severities, rendering the keyword a configuration
|
||||
writes, `err` and `info`, keyed by the numerical code a PRI encodes and selectable by
|
||||
either, so `misc.loglevel[3]` and `misc.loglevel[err]` are one row.
|
||||
`misc.loglevel.severity` is the name RFC 5424 spells, `Error` beside `err`.
|
||||
`misc.loglevel.severity` is the name RFC 5424 spells, `Error` beside `err`; it
|
||||
selects nothing, the code and the keyword do.
|
||||
[`DATA-LICENSES.md`](DATA-LICENSES.md) names each table's source and licence.
|
||||
|
||||
`misc.timezone` is every zone tzdb gives a shipped territory, from one apiece for most
|
||||
@@ -269,7 +270,7 @@ f, err := fejkdata.New(fejkdata.WithSeed(42))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
v, err := f.Fake("sv_SE.address") // "Kungsvägen 68\n379 17 Stockholm"
|
||||
v, err := f.Fake("sv_SE.address") // "Järvedsvägen 43\n891 77 Järved"
|
||||
paths := f.List() // every path Fake accepts, sorted
|
||||
v, err = f.FakeTemplate("name: {/sv_SE.person.last}") // compile + render in one call
|
||||
t, err := f.NewTemplate(`{"format":"name: {x}","x":["bosse","lina"]}`) // compile once
|
||||
@@ -761,7 +762,7 @@ a minor only adds, and a major is the only release that changes what exists.
|
||||
| Surface | Major | Minor |
|
||||
|---------|-------|-------|
|
||||
| Shipped data | remove or rename a path; change a category's format; remove a value, or change a weight or a repeat; add a reference from one shipped category into another; change a table's key, name, weight or parent column, or remove a row | a path outside a record's columns, a locale, a value in a list, a row |
|
||||
| Records | remove, rename, retype or add a column; let a column be null | a record, as a new category |
|
||||
| Records (a table is one) | remove, rename, retype or add a column; let a column be null | a record, as a new category |
|
||||
| Data format | a fence: a spelling `New` rejects that it accepted; a template option, since it reserves a field name | a builtin |
|
||||
| CLI | remove or rename a flag, or change its default; change what an exit code means; change the framing a `--format` writes (header, quoting, statement shape), the `--list` layout, or what an error names | a flag, a format |
|
||||
| Library | change or remove an exported name; raise the lowest supported Go | an exported name, a `With…` option |
|
||||
@@ -800,45 +801,57 @@ the Development section below, and who ships a register the four above then draw
|
||||
|
||||
## Goals
|
||||
|
||||
1. **Valid by construction** — every value passes the check its real consumer
|
||||
1. **The best fake-data tool available, and used because it is** — a developer
|
||||
choosing a seed-data library or CLI, in any language, should find this the one
|
||||
worth switching to, and every goal below it is what that costs. Merit is only
|
||||
half the goal: once the grammar, the data and the CLI are complete and stable,
|
||||
it ships where those developers install from and is announced where they read.
|
||||
2. **Understanding the code is part of the product** — it is a compiler with static
|
||||
fences behind a small API, it will grow, and the people who extend it will not be
|
||||
the people who wrote it. So reading cost is judged alongside behaviour: reaching
|
||||
the unit behind a symptom without asking a person, changing one piece without
|
||||
holding the rest, names that tell the truth, and a file that stands with no second
|
||||
document open beside it. A simulated reader panel scores those four, the score is a
|
||||
ratchet no release lowers, and [`AGENTS.md`](AGENTS.md) says what each pull request
|
||||
owes it.
|
||||
3. **Valid by construction** — every value passes the check its real consumer
|
||||
applies; facts that belong together come from one draw, within a value and
|
||||
across categories.
|
||||
2. **Text means what it says** — a format renders as written; only `{…}` varies,
|
||||
4. **Text means what it says** — a format renders as written; only `{…}` varies,
|
||||
random characters included (`{digits(3)}`). One spelling per result; the wrong
|
||||
one is a load error naming the right one.
|
||||
3. **Every mistake is a load error** — `New` rejects the data and `NewTemplate`
|
||||
5. **Every mistake is a load error** — `New` rejects the data and `NewTemplate`
|
||||
the inline template; on a loaded generator `Fake` fails only for an unknown
|
||||
path, `FakeStruct` only for a non-struct argument or a type its tags do not
|
||||
describe, with the same error every call, and `Template.Fake` cannot fail at all.
|
||||
4. **Zero to a value in one command** — `go install`, then `fejkdata sv_SE.person`:
|
||||
6. **Zero to a value in one command** — `go install`, then `fejkdata sv_SE.person`:
|
||||
no checkout, no flag. Flags are GNU-form (`--seed 42`, `-n 3`) in any position;
|
||||
the first custom template needs no escape and no option.
|
||||
5. **Data lives in JSON** — a builtin only for what data can't express.
|
||||
6. **Reproducible** — seed in, same stream out; no builtin reads a clock.
|
||||
7. **Zero dependencies** — standard library only.
|
||||
8. **Docs index the grammar** — every syntax feature is a heading; every example
|
||||
runs under test and shows its output; a rule is stated once.
|
||||
9. **Fast enough to be free** — a value renders in about a microsecond and `New`
|
||||
parses and validates the whole set once upfront, so generating fixtures stays
|
||||
noise against a test's own runtime.
|
||||
10. **Data is sourced, or on its way there** — a shipped fact, a name, place,
|
||||
7. **Data lives in JSON** — a builtin only for what data can't express.
|
||||
8. **Reproducible** — seed in, same stream out; no builtin reads a clock.
|
||||
9. **Zero dependencies** — standard library only.
|
||||
10. **Docs index the grammar** — every syntax feature is a heading; every example
|
||||
runs under test and shows its output; a rule is stated once.
|
||||
11. **Fast enough to be free** — a value renders in about a microsecond and `New`
|
||||
parses and validates the whole set once upfront, so generating fixtures stays
|
||||
noise against a test's own runtime.
|
||||
12. **Data is sourced, or on its way there** — a shipped fact, a name, place,
|
||||
code, id or classification, is read from a register or open dataset by a
|
||||
[`data-import/`](data-import) script wherever one exists to read; where none
|
||||
does yet a small hand-written set ships and [`todo.md`](todo.md) carries the
|
||||
step that replaces it. Only non-factual copy stays authored. A sourced table
|
||||
holds the rows its source holds: none is added by hand, and one is dropped
|
||||
only by a rule the script states.
|
||||
11. **Breadth follows what most systems store** — a category is added in proportion
|
||||
13. **Breadth follows what most systems store** — a category is added in proportion
|
||||
to how many real schemas hold it: names, addresses, phones, ids, money and
|
||||
timestamps before anything domain-specific, and a catalogue serving one niche
|
||||
waits behind everything serving many.
|
||||
12. **Realism is the default, inertness is selectable** — where a value could reach
|
||||
14. **Realism is the default, inertness is selectable** — where a value could reach
|
||||
something real, a domain anyone may register or an account a bank could issue,
|
||||
the realistic breadth ships *and* so does the subset that provably reaches
|
||||
nothing, each on its own path. A fixture that looks nothing like production
|
||||
tests nothing; the caller who needs a value that can touch nothing asks for it
|
||||
by name.
|
||||
|
||||
## Decisions
|
||||
|
||||
- **Options and fields share one namespace.** `format`, `weight`, `repeat`,
|
||||
@@ -1236,17 +1249,17 @@ the Development section below, and who ships a register the four above then draw
|
||||
fejkdata has no business making.
|
||||
- **`misc.loglevel` is a table keyed by the code, rendering POSIX's keyword.** A flat
|
||||
list of names carries neither the code a PRI encodes nor a selector reaching it, and
|
||||
goal 1 draws the two as one fact. RFC 5424 spells the severity `Informational`,
|
||||
which no syslog, PSR-3 or `slog` checker accepts, so the shipped spelling is the one
|
||||
a configuration writes, `info`, and the RFC's name stays the `severity` column. The
|
||||
code and the keyword take the two selector slots, so `misc.loglevel[Error]` misses.
|
||||
goal 3 draws the two as one fact. The canonical spelling losing to the one its domain
|
||||
writes, above, settles the rest: a configuration writes `info`, so RFC 5424's
|
||||
`Informational` stays the `severity` column, and the code and the keyword hold the
|
||||
two selector slots.
|
||||
- **`misc.tld` keys carry the leading dot, where other tables key on a bare code.**
|
||||
The register spells a TLD `.se` and `misc.territory.tld` already ships it so, which a
|
||||
bare key would make two spellings of one fact; `{/misc.tld}` also composes onto a
|
||||
host with no separator. `misc.tld[se]` misses for it, which `todo.md` carries.
|
||||
- **`misc.tld` is a table of its own, and `misc.territory.tld` stays a column.** A
|
||||
`parent` demands a child for every parent row, so linking them would drop every root
|
||||
zone row naming no territory, which is most of them, and goal 10 holds a sourced
|
||||
zone row naming no territory, which is most of them, and goal 12 holds a sourced
|
||||
table whole. The loader refuses the link outright anyway: `tld` is a column of
|
||||
`misc.territory`, and a table may not be named like a column of its ancestor.
|
||||
- **`misc.territory` carries a currency code, it does not link to `misc.currency`.**
|
||||
@@ -1279,6 +1292,38 @@ the Development section below, and who ships a register the four above then draw
|
||||
through a chain of five tables is every subsequence of it, and the direct chain is
|
||||
the one a reader can predict from the tables' parents.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
fejkdata.go Generator, New, options, the embedded data set, List
|
||||
node.go the node model and JSON -> node compilation
|
||||
table.go tables: the rows TSV, its options and links, row selection and draws
|
||||
path.go the dotted-path walk with its selectors, and proving a path resolves
|
||||
render.go Fake and the recursive renderer (choices, format strings, expansions)
|
||||
record.go records: Record, the JSON/CSV/SQL serializers, and their entry points
|
||||
struct.go structs: FakeStruct, fake tags, and a field's Go type as its column's datatype
|
||||
inline.go inline templates: Template, NewTemplate, FakeTemplate, IsTemplate, and their compile and link
|
||||
template.go the {token} grammar: scanning, tokens, operands, validation, compiling a format
|
||||
hold.go the hold: one draw per expansion for paths and operands, and its fences
|
||||
draw.go one reference draw per render and group: draw sets, the group option, and its fence
|
||||
family.go a family of linked tables: the rows a render pins, and the fence over paths into one family
|
||||
reference.go reference sigils, and binding references across the tree
|
||||
graph.go the render graph: edges, cycles, the repeat bound, tree walks
|
||||
builtins.go the {name()} function registry and its implementations
|
||||
layout.go date and time layouts: the instants one is proved against, and the two samples
|
||||
checksum.go the check characters a derivation appends, and the IBAN they sit inside
|
||||
transform.go the builtins that rewrite an operand's value, and the ASCII folding
|
||||
calc.go the {calc()} arithmetic evaluator: parser, eval, validation
|
||||
datatype.go column datatypes: DataType, where datatype and null may sit, a column's datatype
|
||||
value.go the value proof: what a typed column or calc operand holds, checked at load
|
||||
data.go data loading: fs.FS folders/files -> namespace tree, multi-source merge
|
||||
cmd/fejkdata/ the fejkdata CLI
|
||||
data/ shipped data (JSON, and a TSV per table), embedded at build: locale folders, geo, misc
|
||||
data-import/ the scripts that rebuild each sourced table (see DATA-LICENSES.md)
|
||||
release-tooling/ the release CI publishes from the changelog heading
|
||||
testdata/ the pinned shipped shape (see Versioning)
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
Everything runs in Docker — **no local tooling beyond Docker is needed**.
|
||||
@@ -1366,38 +1411,6 @@ and merge: once `main` passes the gate, CI tags that commit `vX.Y.Z` and publish
|
||||
the Gitea release with the section as its body. A top heading of `[Unreleased]`
|
||||
publishes nothing.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
fejkdata.go Generator, New, options, the embedded data set, List
|
||||
node.go the node model and JSON -> node compilation
|
||||
table.go tables: the rows TSV, its options and links, row selection and draws
|
||||
path.go the dotted-path walk with its selectors, and proving a path resolves
|
||||
render.go Fake and the recursive renderer (choices, format strings, expansions)
|
||||
record.go records: Record, the JSON/CSV/SQL serializers, and their entry points
|
||||
struct.go structs: FakeStruct, fake tags, and a field's Go type as its column's datatype
|
||||
inline.go inline templates: Template, NewTemplate, FakeTemplate, IsTemplate, and their compile and link
|
||||
template.go the {token} grammar: scanning, tokens, operands, validation, compiling a format
|
||||
hold.go the hold: one draw per expansion for paths and operands, and its fences
|
||||
draw.go one reference draw per render and group: draw sets, the group option, and its fence
|
||||
family.go a family of linked tables: the rows a render pins, and the fence over paths into one family
|
||||
reference.go reference sigils, and binding references across the tree
|
||||
graph.go the render graph: edges, cycles, the repeat bound, tree walks
|
||||
builtins.go the {name()} function registry and its implementations
|
||||
layout.go date and time layouts: the instants one is proved against, and the two samples
|
||||
checksum.go the check characters a derivation appends, and the IBAN they sit inside
|
||||
transform.go the builtins that rewrite an operand's value, and the ASCII folding
|
||||
calc.go the {calc()} arithmetic evaluator: parser, eval, validation
|
||||
datatype.go column datatypes: DataType, where datatype and null may sit, a column's datatype
|
||||
value.go the value proof: what a typed column or calc operand holds, checked at load
|
||||
data.go data loading: fs.FS folders/files -> namespace tree, multi-source merge
|
||||
cmd/fejkdata/ the fejkdata CLI
|
||||
data/ shipped data (JSON, and a TSV per table), embedded at build: locale folders, geo, misc
|
||||
data-import/ the scripts that rebuild each sourced table (see DATA-LICENSES.md)
|
||||
release-tooling/ the release CI publishes from the changelog heading
|
||||
testdata/ the pinned shipped shape (see Versioning)
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT — see [LICENSE](LICENSE). Forked from [github.com/Timewave-AB/fakes](https://github.com/Timewave-AB/fakes).
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
|
||||
data-import/loglevel.py [--source URL_OR_FILE] [--posix URL_OR_FILE] [--cache DIR] [--out FILE]
|
||||
|
||||
Table 2 is the whole set, read between the facility table's caption and its own. The
|
||||
keyword is POSIX's severity macro less its `LOG_` prefix, lowercased, taken in the
|
||||
order POSIX lists them, which `LOG_UPTO` states is the severity order; each must be a
|
||||
prefix of the severity RFC 5424 numbers alike, so the two sources pin one pairing.
|
||||
Every severity in Table 2 ships, paired to POSIX's keywords by the order each source
|
||||
lists them, which holds only because a keyword prefixes the severity RFC 5424 numbers
|
||||
alike.
|
||||
"""
|
||||
import argparse
|
||||
import html
|
||||
@@ -24,7 +23,7 @@ CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["code", "keyword", "severity"]
|
||||
TABLE = re.compile(r"Table 1\.\s+Syslog Message Facilities(.*?)Table 2\.\s+Syslog Message Severities", re.S)
|
||||
ROW = re.compile(r"^ +(\d) +([A-Z][a-z]+): ", re.M)
|
||||
SEVERITIES = re.compile(r"severity level portion of the.*?macros:(.*?)The following shall be declared as functions", re.S)
|
||||
SECTION = re.compile(r"severity level portion of the.*?macros:(.*?)The following shall be declared as functions", re.S)
|
||||
MACRO = re.compile(r"\bLOG_([A-Z]+)\b")
|
||||
EXPECTED = 8
|
||||
|
||||
@@ -37,7 +36,7 @@ def severities(text):
|
||||
|
||||
|
||||
def keywords(page):
|
||||
section = SEVERITIES.search(html.unescape(re.sub(r"<[^>]+>", "", page)))
|
||||
section = SECTION.search(html.unescape(re.sub(r"<[^>]+>", "", page)))
|
||||
if not section:
|
||||
sys.exit("POSIX spells no severity list between its own caption and the function declarations; the page has moved")
|
||||
return [m.lower() for m in MACRO.findall(section.group(1))]
|
||||
|
||||
+6
-6
@@ -5,8 +5,8 @@
|
||||
// then generate values by path:
|
||||
//
|
||||
// f, _ := fejkdata.New(fejkdata.WithSeed(42))
|
||||
// f.Fake("sv_SE.address") // "Storgatan 12\n234 56 Göteborg"
|
||||
// f.Fake("sv_SE.address.locality") // "Göteborg"
|
||||
// f.Fake("sv_SE.address") // "Järvedsvägen 43\n891 77 Järved"
|
||||
// f.Fake("sv_SE.address.locality") // "Sundbyberg": a second call draws afresh
|
||||
//
|
||||
// Several sources merge in order, the last winning a name clash, so custom data
|
||||
// layers over the built-ins. The JSON template format is documented in the README.
|
||||
@@ -29,8 +29,8 @@ import (
|
||||
//go:embed data
|
||||
var shippedFS embed.FS
|
||||
|
||||
// MaxRepeat caps a repeat, and the renders nested repeats multiply to along any
|
||||
// path; the CLI's --repeat shares it.
|
||||
// MaxRepeat caps a repeat, and caps the renders nested repeats multiply to along
|
||||
// any one path; the CLI's --repeat shares it.
|
||||
const MaxRepeat = 1 << 20
|
||||
|
||||
var _ [^uint(0)>>63 - 1]struct{} // 64-bit only, per the README's Decisions
|
||||
@@ -40,8 +40,8 @@ var ErrNoData = errors.New("no data: WithoutShippedData needs at least one WithD
|
||||
|
||||
// Generator generates fake data from a loaded namespace tree. Create one with [New].
|
||||
// It is safe for concurrent use; a seeded sequence is reproducible only when drawn
|
||||
// from one goroutine. The compiled tree is immutable after [New], and Fake,
|
||||
// NewTemplate and List read it concurrently without a lock.
|
||||
// from one goroutine. The compiled tree is immutable after [New], so List reads it
|
||||
// without a lock; mu guards the rest, which every other entry point takes.
|
||||
type Generator struct {
|
||||
mu sync.Mutex
|
||||
rand *session
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// node is a compiled template element: a choice or a template. Compiling JSON
|
||||
// into these once (see compile) means rendering never re-inspects the raw JSON or
|
||||
// re-sums weights.
|
||||
// node is a compiled element of the namespace tree: a folder, choice, null,
|
||||
// template, table, column or row. Compiling JSON into these once (see compile)
|
||||
// means rendering never re-inspects the raw JSON or re-sums weights.
|
||||
type node interface{ isNode() }
|
||||
|
||||
// folder is a namespace of named children, built from a directory of JSON files
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// rng is the randomness the renderer draws from. Passing it in keeps the render
|
||||
// functions a pure core over an explicit effect; *rand.Rand satisfies it.
|
||||
// rng is the randomness a builtin sample draws from; *rand.Rand satisfies it. The
|
||||
// render path takes the concrete *session instead, for the reason below.
|
||||
type rng interface {
|
||||
IntN(n int) int
|
||||
Float64() float64
|
||||
|
||||
@@ -23,7 +23,7 @@ type table struct {
|
||||
whole *row // the pinned row rendered by the format
|
||||
cells []string // rows × columns, flat
|
||||
tokens map[int]*template
|
||||
key int // column index, or -1
|
||||
key int // key, name, weight and parent are column indexes, or -1
|
||||
name int
|
||||
weight int
|
||||
parent int
|
||||
|
||||
@@ -4,10 +4,53 @@ Ordered as the releases that ship it.
|
||||
|
||||
## v0.1.0
|
||||
|
||||
### Comprehension, until the panel scores 7.0
|
||||
|
||||
Nothing else ships while the score is under 7.0 — no feature, no category, no data —
|
||||
bar a security fix and a dependency bump, per README goal 2. This section is one
|
||||
round, replayed: the `comprehension-panel` skill's nine-seat findings run at depth 1,
|
||||
a PR per item it names, then the run again, until the score passes. The run is the
|
||||
nine-seat one: the round is meant to buy architectural change, and the four-seat
|
||||
scoring run names too little to steer one. A fresh run replaces the items below.
|
||||
|
||||
Scores, newest last: 5.9 on 2026-09-20 (Navigation 7.0, Locality 5.2, Shape 5.9,
|
||||
Self-sufficiency 5.7), eight of nine seats capped by Locality or Self-sufficiency, all
|
||||
nine naming `hold.go`, `draw.go` and `family.go` first for reading cost: 1,138 lines in
|
||||
which each file carries both a load-time fence and render-time machinery, so no file
|
||||
name says whether a unit runs at `New` or at `Fake`.
|
||||
|
||||
This round, in order:
|
||||
|
||||
- Define the fence vocabulary where a reader of the code meets it: fence, hold, pin,
|
||||
draw, draw group, expansion against render, family, whole. It exists only across five
|
||||
README sections today, and every reader paid for it before the three files read at all.
|
||||
- Say on the `template` struct which pass fills which field, and that `compileFormat`
|
||||
runs a second time once `linkTemplateRefs` binds the references: nine of its fields
|
||||
are written by four other files, and the order lives only in `loadData`'s body.
|
||||
- State on `rowSet` that it simulates `(*draws).pin` and `rowOf`, and prove the two
|
||||
agree with a test: the load-time walk reimplements the render-time pinning, nothing
|
||||
catches them drifting, and the failure is data that loads and then renders a family
|
||||
that disagrees.
|
||||
- Put `draws` and its methods in one file: it is declared in `hold.go:262` and all
|
||||
eight of its methods sit in `family.go`, so a jump to the type lands where none of
|
||||
the behaviour is.
|
||||
- Split `hold.go`, `draw.go` and `family.go` so each file holds one phase, the
|
||||
load-time fences apart from the render-time machinery, changing no logic.
|
||||
- Move `nodeScope`, `checkScope` and `checkRenders` out of `graph.go` and refresh the
|
||||
README Layout lines for `graph.go` and `data.go`: the order every load fence runs in
|
||||
is the load path's spine, and no file name carries it.
|
||||
- Name the `draw` stem apart: the free `pinned` in `hold.go` is not `(*draws).pinned`
|
||||
in `family.go`, `compileFormat` is a method on both `*template` and `*table`, and
|
||||
`draws`, `drawSet`, `drawScope`, `drawWalk`, `drawAt`, `drawKey`, `drawVisit` and
|
||||
`drawRoute` no longer tell each other apart.
|
||||
- Spell one fresh draw set one way: `newDrawSet` exists, and `render.go` and `draw.go`
|
||||
hand-roll `drawSet{unnamed: draws{s: s}}` at three sites; name the lazy-map variant
|
||||
if the difference is deliberate.
|
||||
|
||||
### Data
|
||||
|
||||
Shape: T = table, t = template, c = choice. Read every factual list from a register
|
||||
by a `data-import/` script, as README goal 10 asks.
|
||||
by a `data-import/` script, as README goal 12 asks.
|
||||
|
||||
- Add the remaining `misc` tables and templates, one row of the table below per chunk.
|
||||
- Read `misc.emoji` from the Unicode `emoji-test.txt` register, and `misc.car` from an
|
||||
@@ -20,16 +63,25 @@ by a `data-import/` script, as README goal 10 asks.
|
||||
`municipality` as columns on `geo.SE.address` too.
|
||||
- Fill the 398 Swedish localities weighted 200 from SCB småorter.
|
||||
- Give `url` and `email` a path that draws only domains nobody can register, keeping
|
||||
the wide set as the default, per goal 12: 17 of the 40 distinct ones shipped today
|
||||
the wide set as the default, per goal 14: 17 of the 40 distinct ones shipped today
|
||||
sit on `.se`, `.nu`, `.io` and `.co`, which anyone may register, and only RFC 2606's
|
||||
`example.com`, `.net`, `.org`, `.test`, `.example`, `.invalid` and `.localhost`
|
||||
provably reach nothing.
|
||||
- Audit the rest of the shipped set against goal 12 and give each a never-reaching
|
||||
- Audit the rest of the shipped set against goal 14 and give each a never-reaching
|
||||
path where it lacks one: `phone` draws live PTS and NANP ranges, `bankgiro`,
|
||||
`plusgiro` and `routing` draw live prefixes.
|
||||
- Stop `misc.territory[EH].tld` rendering `.eh`, the one shipped TLD `misc.tld` does
|
||||
not hold: ISO 3166 reserves it for Western Sahara and the root zone has never been
|
||||
delegated it, so no resolver answers for it and goal 1 promises otherwise.
|
||||
delegated it, so no resolver answers for it and goal 3 promises otherwise.
|
||||
- Draw `en_US.phone`'s `exch` as a NANP central office code: `{int(100,999)}` renders
|
||||
a leading 1 in about an eighth of draws, which libphonenumber rejects, and
|
||||
`TestShippedUSPhone` proves the shape rather than the rule. Assert the rule with it.
|
||||
- Give a `sv_SE.personnummer` over 100 the `+` separator Skatteverket spells, or stop
|
||||
drawing birthdates that reach 100: the format hard-codes `-`, and the 1930 floor
|
||||
makes the oldest draws invalid from 2031.
|
||||
- Settle `en_US.ip` and `sv_SE.ip`, today byte-identical: either one `misc.ip` as the
|
||||
decision "`misc` is what every locale shares" asks, or a decision saying why an
|
||||
address that carries no locale stays per-locale.
|
||||
- Accept a middle name, and draw a shipped `personnummer` inside a *selected* sex;
|
||||
both want a draw group sharing its family's pins. Stop the conflict error naming a
|
||||
rewrite that returns a different value where the read it conflicts with sits inside
|
||||
@@ -39,7 +91,7 @@ by a `data-import/` script, as README goal 10 asks.
|
||||
beside the family: require the path step to reach a sibling category, and seed every
|
||||
draw group of the render from the outer selector's pins.
|
||||
- Read `email.local`'s and `username`'s handles from the shipped name tables, which
|
||||
goal 10 asks for and the hand-written list they use today does not meet. Keep the
|
||||
goal 12 asks for and the hand-written list they use today does not meet. Keep the
|
||||
handle shape: `username` shortens a surname to `ahl` or `sjo`, and the US one draws
|
||||
unisex given names, neither of which falls out of the tables unaided.
|
||||
- Decide whether `email.local` and `username` share one list: a reference between
|
||||
@@ -110,8 +162,9 @@ by a `data-import/` script, as README goal 10 asks.
|
||||
`misc.httpmethod[get]` and `misc.territory[se]` all answer "no row … has key or name"
|
||||
and stop there, where a case-insensitive match could name the row that exists, and a
|
||||
table with no `name` column could say it selects by key alone. `misc.tld[se]` misses
|
||||
on the leading dot its keys carry rather than on case, so the near miss is worth
|
||||
naming whatever shape it takes.
|
||||
on the leading dot its keys carry rather than on case, and `misc.loglevel[Error]`
|
||||
misses although a column carries `Error`, so the near miss is worth naming whatever
|
||||
shape it takes.
|
||||
- Let a table column carry a `datatype`, so `--format json` writes
|
||||
`"safe": true` and `--format sql` a boolean rather than the text `'true'`. Today only
|
||||
a JSON field takes one, so `misc.httpmethod`'s booleans are typed in Go and text
|
||||
@@ -120,6 +173,12 @@ by a `data-import/` script, as README goal 10 asks.
|
||||
|
||||
### Open questions to settle
|
||||
|
||||
- Decide where the module is found from, before goal 1's announcement: the import
|
||||
path is `gitea.larvit.se/larvit/fejkdata`, a developer looking for a fake-data
|
||||
library searches GitHub and pkg.go.dev, and a module path is a major to change.
|
||||
- Decide whether `data/misc`'s 33 flat files gain a level before v1.0.0: a folder is a
|
||||
path segment, so `misc/net/tld` is a rename a consumer pays for, and goal 13 promises
|
||||
the directory keeps growing.
|
||||
- Decide whether `misc.browser` becomes a parent of `misc.useragent`, so
|
||||
`misc.browser[Chrome].useragent` resolves. Adding a `parent` after v0.1.0 breaks a
|
||||
consumer, so it rides a 0.(x+1).0.
|
||||
@@ -174,8 +233,11 @@ because pairing a street with its exact postnummer rewrites shipped rows.
|
||||
## v1.0.0
|
||||
|
||||
- Cut it once the shipped data is in its record shape and one full minor has shipped
|
||||
with no breaking change, per the README's Versioning table. It plans no work of its
|
||||
own: v0.1.0 settles the record shape and v0.3.0 is the quiet minor.
|
||||
with no breaking change, per the README's Versioning table: v0.1.0 settles the
|
||||
record shape and v0.3.0 is the quiet minor.
|
||||
- Announce it where a developer choosing a fake-data tool already reads, which is
|
||||
goal 1's second half and waits for the tag: complete, stable, installable without
|
||||
Go. Write the README's first screen for someone deciding in a minute.
|
||||
|
||||
## Not release-bound
|
||||
|
||||
|
||||
Reference in New Issue
Block a user