WithDefault + cyclic-build guard (2.1.0)#1
Open
jlamfers2 wants to merge 8 commits into
Open
Conversation
WithDefault<TValue>(x => x.Member) is the strongly-typed equivalent of the "default()" string token for a nested model member: it fills the member via that type's own default builder. Composing it in each builder's SetDefaults lets an object graph fill itself, one explicit level per builder. Relax the WithBuilder constraint from `class` to `class?` for the same reason, so both typed reference-member helpers no longer warn on nullable-annotated members. Add a thread-local reentrancy guard in ModelBuilder.Build(): because every nested build funnels through a builder's Build(), a model type that (transitively) builds itself - via WithDefault, WithBuilder, a "default()" or named-builder value, an auto-vivified nested path, or a built list element - now throws a clear InvalidOperationException with the cycle path instead of an unrecoverable StackOverflowException. Sequential same-type builds (BuildMany) and independent same-type siblings are not affected. Tests cover WithDefault (ad-hoc, in SetDefaults, ctor routing, "default()" equivalence, string->null, deep composition) and the guard (mutual and self cycles across all triggering mechanisms, sibling non-cycle, post-throw recovery, BuildMany). Docs (README.md/README-nl.md) updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment Thanks for integrating Codecov - We've got you covered ☂️ |
Add the published dev.to introduction article under docs/, and link to it from the top of README.md and README-nl.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New dependency-free faker package for Netherlands-specific test data, following the same IFaker namespace convention as XFaker/Bogus: tokens under `nl.*` and the `xprovider.Nl()` accessor. Generators (valid checks where applicable): - Bsn, Rsin, BtwNummer, Bankrekeningnummer -> elfproef (11-test) - Iban -> ISO 13616 mod-97 - KvkNummer, Vestigingsnummer, AgbCode - Postcode, Kenteken, Mobiel, VastTelefoonnummer - Paspoortnummer, Rijbewijsnummer, Provincie, Gemeente Gemeente() uses the full current CBS/Wikipedia list of all 342 Dutch municipalities (peildatum 1-1-2024). Registers its own seeded Random (like the Bogus integration) so its seed stays independent from XFaker. Includes 16 unit tests (checksums, formats, determinism, coexistence with XFaker, token resolution), README EN/NL chapter 21.4, and the CLAUDE.md solution-layout row. CI/packaging need no change: publish.yml packs the whole solution. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Aligns the recursion-guard test models/builders with the repository convention that code, comments and identifiers are in English. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DutchFakerApi.Kenteken() generates the real Dutch sidecode XXX-99-X (LLL-DD-L), but the test's allowed-pattern regex omitted it, so CI hit plates like "XVX-62-T" and failed. Add the missing alternative. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follows the .NET naming guideline that two-letter acronyms are all uppercase (like System.IO). Renames the namespace property DutchFaker.Nl and the provider accessor xprovider.Nl() to NL, plus the README refs. Tokens stay lowercase and case-insensitive (nl.*), so token usage is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Core (XModelBuilder): two public, country-agnostic helpers so any IFaker
can generate valid-checksum identifiers in one line:
- RandomExtensions: Digits, PickFrom, FromPattern ('#'=digit, '?'=letter)
- Checksums: Mod11WeightedSum/IsValid (elfproef family, ISBN-10, ...),
Luhn (cards/IMEI), Gs1 (EAN/GTIN/SSCC), Mod97 (IBAN)
DutchFakerApi now builds on these helpers (thin wrappers) and adds:
- Bic, EanCode (GS1-valid), BigNummer, UzoviCode, Geslacht
The refactor preserves the RNG draw order, so existing seeded values are
unchanged. Adds ChecksumsTests + RandomExtensionsTests (core) and tests
for the new NL methods; README EN/NL chapter 21.4 + file tables updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WithDefault<TValue>(x => x.Member)— the strongly-typed equivalent of the"default()"string token for a nested model member: fills the member via that type's own default builder (itsSetDefaultsruns). Composing it in each builder'sSetDefaultslets an object graph fill itself, one explicit level per builder — no framework-wide "build deep" magic. It is todefault()whatWithBuilderis to a named-builder reference.WithBuilderconstraint relaxedclass→class?— same rationale, so both typed reference-member helpers no longer warn on nullable-annotated members (e.g.Address?). Backward-compatible (a relaxation).ModelBuilder.Build(). Because every nested build funnels through a builder'sBuild(), a model type that (transitively) builds itself — viaWithDefault,WithBuilder, a"default()"or named-builder value, an auto-vivified nested path, or a built list element — now throws a clearInvalidOperationException(with the cycle path) instead of an unrecoverableStackOverflowException. Not a false-positive risk: declarative builders mean a re-entered type is genuinely infinite. Sequential same-type builds (BuildMany) and independent same-type siblings are unaffected.Tests
WithDefaultTests— ad-hoc, inSetDefaults, ctor routing,"default()"equivalence,string→null, deep composition.BuildRecursionGuardTests— mutual (Vader→Kind→Vader) and self cycles across all triggering mechanisms (WithDefault,WithBuilder,default()token, named-builder reference), same-typed siblings are not a cycle, post-throw recovery, andBuildMany(no false cycle across sequential builds; cyclic type throws).All unit-test projects green (core 316, Fakers 24, Reqnroll 13, SpecFlow 12); solution builds with 0 warnings.
Docs
README.md+README-nl.mdupdated: chapter 6h (WithDefault), chapter 15 (cyclic-build guard), chapter 16 (Core file table), chapter 17 signatures.Note for reviewers
Adding
WithDefaultto the publicIModelBuilder<TModel>/IModelBuilderinterfaces is treated as a minor bump (onlyModelBuilder<,>implements them; they are not consumer-implemented contracts). If you consider those interfaces externally implementable, this would instead warrant a major bump.🤖 Generated with Claude Code