From 87a2e7f5e56972c66a9e630ac3af3facf58d7d74 Mon Sep 17 00:00:00 2001 From: Mateusz Kubuszok Date: Mon, 6 Nov 2023 12:12:12 +0100 Subject: [PATCH] Add goals-and-non-goals section, improve TOC in troubeshootings, fix a few errors in docs --- docs/docs/supported-transformations.md | 10 ++--- docs/docs/troubleshooting.md | 56 +++++++++++++++++++++----- docs/docs/under-the-hood.md | 2 +- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/docs/docs/supported-transformations.md b/docs/docs/supported-transformations.md index 8ba826819..92ccba760 100644 --- a/docs/docs/supported-transformations.md +++ b/docs/docs/supported-transformations.md @@ -992,7 +992,7 @@ We are also able to provide values in nested structure: .withFieldRenamed(_.foo, _.bar) .withFieldConst(_.bar.c, 1000L) .transform.asEither // Right(NestedBar(Bar("value", 1248, 1000L))) - ``` + ``` ### Wiring the constructor's parameter to the computed value @@ -1075,7 +1075,7 @@ The requirements to use a value computation are as follows: setters are enabled) The last conditions is always met when working with `case class`es with no `private val`s in constructor, and classes -with all arguments declared as public `val`s, and Java Beans where each setter has corresponding getter defined. +with all arguments declared as public `val`s, and Java Beans where each setter has a corresponding getter defined. !!! example @@ -1222,8 +1222,8 @@ as transparent, similarly to virtually every other Scala library. !!! warning - If you use any override (`withFieldConst`) getting value from/to `AnyVal`, it _will_ be treated as just a normal - product type. + If you use any value override (`withFieldConst`, `withFieldComputed`, etc.) getting value from/to `AnyVal`, it + _will_ be treated as just a normal product type. ## Between `sealed`/`enum`s @@ -1778,7 +1778,7 @@ or having type parameter being not used at all: Foo[AbstractType1]("value").transformInto[Bar[AbstractType2]] // Bar[AbstractType2]("value") ``` -If the type is `abstract`, used as a value, but contains enough information that one of existing rules +If the type is `abstract` and used as a value, but contains enough information that one of existing rules knows how to apply it, the transformation can still be derived: !!! example diff --git a/docs/docs/troubleshooting.md b/docs/docs/troubleshooting.md index 77b676c21..e1dcdd23f 100644 --- a/docs/docs/troubleshooting.md +++ b/docs/docs/troubleshooting.md @@ -2,6 +2,34 @@ Already using Chimney and you've got some issues? This page might help you with it. +## Goals and non-goals + +While Chimney is usually used to convert one piece of immutable data into another piece of immutable data, not every +time you are working with immutable values you need Chimney. + +The main goal of Chimney is minimizing the amount of code you have to write to convert a value of one type (known at +compile time) into a value of another type (also known at compile time). While there are Patchers, their goal is to +update one value, using another value and nothing more. + +If you: + + - receive some unstructured, raw input value (String, InputStream, binary data, ...) you are not looking for Chimney + when it comes to parsing it - you need a parser library. Maybe it will be a JSON parser, or something you could + build with parser combinators, or a code generated with Interface Description Language (IDL) like Protocol Buffers, + Swagger or AsyncAPI. + - However, if you parse raw data into some structured data, that structured data can be used by Chimney to convert + into e.g. domain model + - want to update immutable data by passing some path to the updated field and then provide a value - you need a lens + library like Quicklens or Monocle + - want to limit the amount of tests written and are wondering if automatic generation of such an important code is + safe - you need to ask yourself: would you have the same dilemma if you were asked about generating JSON codecs? + Would you wonder if you need to test them? (In our experience, yes). Could you remove the need to test them if you + mandated the code to be always/never generated? (In our experience, whether its generated or written by hand you + want to test it). + - You can however avoid testing someone else's library if you use Chimney in place, in some service, and then test + that service's behavior or if you create in your DTO model `def toDomain = this.transformInto[DomainModel]`. + You can utilize code generation without making your application's type signatures depend on someone else's types. + ## Migration from 0.7.x to 0.8.0 Version 0.8.0 is the first version that cleaned up the API. It introduced several breaking changes. @@ -220,7 +248,15 @@ if there is no source field nor other fallback or override. Although it is a bugfix, it is also a breaking change so it has to be documented. The fix would be a manual resolution for all fields which now (correctly) fail due to the bugfix. -## Recursive types fail to compile +## Compilation errors + +When some transformation cannot be generated with the information available to the library, it is perfectly normal that +a macro would generate a compilation error with a message describing the issue. + +However, some compilation errors might seem unreasonable as everything seems to be configured correctly for a use case +that is officially supported. + +### Recursive types fail to compile Chimney attempts to avoid unnecessary memory allocations for good performance. @@ -254,7 +290,7 @@ and then The same is true for partial transformers. -## Recursive calls on implicits +### Recursive calls on implicits Old versions of Chimney in situations like this: @@ -290,7 +326,7 @@ It's a sign of recursion which has to be handled with [semiautomatic derivation] implicit val t: Transformer[Foo, Bar] = Transformer.define[Foo, Bar].buildTransformer ``` -## `sealed trait`s fail to recompile +### `sealed trait`s fail to recompile In the case of incremental compilation, the Zinc compiler sometimes has issues with caching certain kind of information and macros don't get proper information @@ -301,7 +337,7 @@ the compiler to provide it with this data, and the compiler fails to do so. On Scala 2.12.0 it failed [in other cases as well (scala/bug#7046)](https://github.com/scala/bug/issues/7046), so it is recommended to update 2.12 to at least 2.12.1. -## Scala 3 complains that `implicit`/`given` `TransformerConfiguration` needs an explicit return type +### Scala 3 complains that `implicit`/`given` `TransformerConfiguration` needs an explicit return type In Scala 2 syntax like @@ -336,7 +372,7 @@ You can work around this by slightly longer incantation: TransformerConfiguration.default.enableMacrosLogging ``` -## `java.lang.UnsupportedOperationException: Position.point on NoPosition` error +### `java.lang.UnsupportedOperationException: Position.point on NoPosition` error On Scala 2 `java.lang.UnsupportedOperationException: Position.point on NoPosition` is most commonly seen due to [scala/bug#10604](https://github.com/scala/bug/issues/10604) - when JVM used for compilation has a small stack trace @@ -350,7 +386,7 @@ However, if you are using the compiler's flags to report unused definitions when an error caused by [scala/bug#12895](https://github.com/scala/bug/issues/12895). In such case the workaround would be to remove the unused definition reporting. -## Debugging macros +### Debugging macros In some cases, it could be helpful to preview what is the expression generated by macros, which implicits were used in macro (or not) and what was the exact @@ -583,10 +619,10 @@ would generate: + Rule Subtypes expanded successfully: userupdateform.phone + Derived recursively total expression userupdateform.phone + Rule TypeToValueClass expanded successfully: new Phone(userupdateform.phone) - + Derived final expression is: - | new User(user.id, new Email(userupdateform.email), new Phone(userupdateform.phone)) - + Derivation took 0.028971000 s - ``` + + Derived final expression is: + | new User(user.id, new Email(userupdateform.email), new Phone(userupdateform.phone)) + + Derivation took 0.113354000 s + ``` ## Ideas, questions or bug reports diff --git a/docs/docs/under-the-hood.md b/docs/docs/under-the-hood.md index 8a55b25c0..202238c9b 100644 --- a/docs/docs/under-the-hood.md +++ b/docs/docs/under-the-hood.md @@ -461,7 +461,7 @@ Chimney would have specialized expression generation for things like: these collections to accumulate errors and add indices/keys to the error information For each of these `Rule` might decide whether the case should be handled. Some of these rules needs to be tested before -some other rules (e.g. there might be several `Rule`s for `AnyVal`s with specific order) and have no requirements about +some other rules (e.g. there might be several `Rule`s for `AnyVal`s with a specific order) and have no requirements about other rules (e.g. `Rule`s handling `Option`s might be unrelated to `Rule`s handling `AnyVal`s and not affect each other). #### Product types