-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add NullOnCircularReference strategy to NullableStrategy #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
MessiasLima
merged 9 commits into
main
from
feat/null-on-circular-reference-5388625712608883014
May 8, 2026
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
f41266d
feat: add NullOnCircularReference strategy as default
google-labs-jules[bot] 550a3d8
feat: add NullOnCircularReference strategy as default
google-labs-jules[bot] 338ef46
docs: SomeConfig kdocs
MessiasLima 406f3b1
feat: enhance circular reference handling
MessiasLima 421c7eb
fix: always null behaviour
MessiasLima d92fd8b
chore: ignore reference folder
MessiasLima 957b781
refactor: enhance circular reference detection
MessiasLima c478326
docs: add kdocs
MessiasLima b1c6b85
refactor: SomeConfig immutable
MessiasLima File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| docs/reference |
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,44 +1,106 @@ | ||
| package dev.appoutlet.some | ||
|
|
||
| import dev.appoutlet.some.config.SomeConfig | ||
| import dev.appoutlet.some.config.SomeConfigBuilder | ||
| import dev.appoutlet.some.core.ResolverChain | ||
| import dev.appoutlet.some.core.TypeResolver | ||
| import kotlin.random.Random | ||
| import kotlin.reflect.typeOf | ||
|
|
||
| /** | ||
| * Fixture generator configured with a resolver chain and shared random source. | ||
| * | ||
| * Instances are created by [someSetup] and can be reused to generate multiple values with the same configuration. | ||
| * | ||
| * @param resolvers Ordered resolver list used to generate values. | ||
| * @param random Random source shared by resolvers created for this instance. | ||
| * @param config Configuration used by this generator. | ||
| */ | ||
| class Some( | ||
| val resolvers: List<TypeResolver>, | ||
| val random: Random, | ||
| val config: SomeConfig | ||
| ) { | ||
| /** | ||
| * Generates a fixture value of type [T] using this instance's configuration. | ||
| * | ||
| * @param T Type to generate. | ||
| * @return Generated value of type [T]. | ||
| */ | ||
| @Suppress("MemberNameEqualsClassName") | ||
| inline fun <reified T> some(): T { | ||
| val session = ResolverChain(resolvers) | ||
| val session = ResolverChain(resolvers, config.nullableStrategy) | ||
| return session.resolve(typeOf<T>()) as T | ||
| } | ||
|
|
||
| /** | ||
| * Generates a fixture value of type [T]. | ||
| * | ||
| * Enables concise usage such as `some<User>()` when `some` is a [Some] instance. | ||
| * | ||
| * @param T Type to generate. | ||
| * @return Generated value of type [T]. | ||
| */ | ||
| inline operator fun <reified T> invoke(): T = some() | ||
|
|
||
| inline operator fun <reified T> invoke(noinline config: SomeConfig.() -> Unit = {}): T { | ||
| val aggregatedConfig = this.config.copy().apply(config) | ||
| /** | ||
| * Generates a fixture value of type [T] with per-call configuration overrides. | ||
| * | ||
| * Overrides are applied to a copy of this instance's configuration and do not mutate the original [Some]. | ||
| * | ||
| * @param T Type to generate. | ||
| * @param config Configuration overrides for this call. | ||
| * @return Generated value of type [T]. | ||
| */ | ||
| inline operator fun <reified T> invoke(noinline config: SomeConfigBuilder.() -> Unit = {}): T { | ||
| val aggregatedConfig = this.config.toBuilder().apply(config).build() | ||
| return Some(aggregatedConfig.buildResolvers(random), random, aggregatedConfig).some() | ||
| } | ||
| } | ||
|
|
||
| fun someSetup(config: SomeConfig.() -> Unit = {}): Some { | ||
| val someConfig = SomeConfig().apply(config) | ||
| /** | ||
| * Creates a reusable [Some] generator. | ||
| * | ||
| * Use this when multiple fixtures should share the same configuration and random source. | ||
| * | ||
| * @param config Configuration applied to the created generator. | ||
| * @return A configured [Some] instance. | ||
| */ | ||
| fun someSetup(config: SomeConfigBuilder.() -> Unit = {}): Some { | ||
| val someConfig = SomeConfigBuilder().apply(config).build() | ||
| val random = someConfig.buildRandom() | ||
| return Some(someConfig.buildResolvers(random), random, someConfig) | ||
| } | ||
|
|
||
| val defaultResolvers: List<TypeResolver> by lazy { SomeConfig().buildResolvers() } | ||
| /** | ||
| * Lazily-created default configuration used by top-level [some]. | ||
| */ | ||
| val defaultConfig: SomeConfig by lazy { SomeConfig() } | ||
|
|
||
| /** | ||
| * Lazily-created default resolver chain used by top-level [some]. | ||
| */ | ||
| val defaultResolvers: List<TypeResolver> by lazy { defaultConfig.buildResolvers() } | ||
|
|
||
| /** | ||
| * Generates a fixture value using the default configuration. | ||
| * | ||
| * @param T Type to generate. | ||
| * @return Generated value of type [T]. | ||
| */ | ||
| inline fun <reified T> some(): T { | ||
| val session = ResolverChain(defaultResolvers) | ||
| val session = ResolverChain(defaultResolvers, defaultConfig.nullableStrategy) | ||
| return session.resolve(typeOf<T>()) as T | ||
| } | ||
|
|
||
| inline fun <reified T> some(noinline config: SomeConfig.() -> Unit = {}): T { | ||
| /** | ||
| * Generates a fixture value using one-off configuration overrides. | ||
| * | ||
| * @param T Type to generate. | ||
| * @param config Configuration applied only to this generation call. | ||
| * @return Generated value of type [T]. | ||
| */ | ||
| inline fun <reified T> some(noinline config: SomeConfigBuilder.() -> Unit = {}): T { | ||
| val someSetup = someSetup(config) | ||
| return someSetup.some<T>() | ||
| } | ||
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
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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that
defaultResolversis lazily initialized using the initial state ofdefaultConfig. SinceSomeConfigproperties (likenullableStrategy) are mutable, any changes made todefaultConfigafterdefaultResolvershas been accessed will not be reflected in the existing resolvers. This creates a mismatch where theResolverChainuses the updated strategy but the resolvers continue using the old one. Consider makingSomeConfigimmutable or refactoring resolvers to access strategies dynamically from the session context.