Supported Patching
Besides transforming Chimney is also able to perform patching - take a "patched" value, a "patch" value, and compute the updated version of the patched value.
Updating case class
The main supported case is updating one case class with another:
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class Email(address: String) extends AnyVal
case class Phone(number: Long) extends AnyVal
case class User(id: Int, email: Email, phone: Phone)
case class UserUpdateForm(email: String, phone: Long)
val user = User(10, Email("abc@@domain.com"), Phone(1234567890L))
val updateForm = UserUpdateForm("xyz@@domain.com", 123123123L)
pprint.pprintln(
user.patchUsing(updateForm)
)
// expected output:
// User(id = 10, email = Email(address = "xyz@@domain.com"), phone = Phone(number = 123123123L))
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[User, UserUpdateForm] = Patcher.derive[User, UserUpdateForm]
// or (if you want to pass overrides):
val patcher2: Patcher[User, UserUpdateForm] = Patcher.define[User, UserUpdateForm]
.buildPatcher
As we see the values from the "patch" aren't always of the same type as the values they are supposed to update.
In such case, macros use Transformers logic under the hood to convert a patch into a patched value.
Updating field with a provided value
When we want to not only update one object with fields from another object but also set some fields manually,
we can do it using .withFieldConst (just like with Transformers):
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class Email(address: String) extends AnyVal
case class Phone(number: Long) extends AnyVal
case class User(id: Int, email: Email, phone: Phone)
case class UserUpdateForm(email: String, phone: Long)
val user = User(10, Email("abc@@domain.com"), Phone(1234567890L))
val updateForm = UserUpdateForm("xyz@@domain.com", 123123123L)
pprint.pprintln(
user.using(updateForm)
.withFieldConst(_.id, 20)
.patch
)
// expected output:
// User(id = 20, email = Email(address = "xyz@@domain.com"), phone = Phone(number = 123123123L))
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[User, UserUpdateForm] = Patcher.define[User, UserUpdateForm]
.withFieldConst(_.id, 20)
.buildPatcher
Updating field with a computed value
When we want to not only update one object with fields from another object but also set some fields to computed value,
we can do it using .withFieldComputed (just like with Transformers) or .withFieldComputedFrom:
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class Email(address: String) extends AnyVal
case class Phone(number: Long) extends AnyVal
case class User(id: Int, email: Email, phone: Phone)
case class UserUpdateForm(email: String, phone: Long)
case class Wrapper[A](value: A)
val wrappedUser = Wrapper(User(10, Email("abc@@domain.com"), Phone(1234567890L)))
val updateForm = Wrapper(UserUpdateForm("xyz@@domain.com", 123123123L))
pprint.pprintln(
wrappedUser.using(updateForm)
.withFieldComputed(_.value.id, patch => patch.value.phone.toInt)
.patch
)
// expected output:
// Wrapper(
// value = User(
// id = 123123123,
// email = Email(address = "xyz@@domain.com"),
// phone = Phone(number = 123123123L)
// )
// )
pprint.pprintln(
wrappedUser.using(updateForm)
.withFieldComputedFrom(_.value.phone)(_.value.id, phone => phone.toInt)
.patch
)
// expected output:
// Wrapper(
// value = User(
// id = 123123123,
// email = Email(address = "xyz@@domain.com"),
// phone = Phone(number = 123123123L)
// )
// )
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[Wrapper[User], Wrapper[UserUpdateForm]] = Patcher.define[Wrapper[User], Wrapper[UserUpdateForm]]
.withFieldComputed(_.value.id, patch => patch.value.phone.toInt)
.buildPatcher
Ignoring fields in patches
When the patch case class contains a field that does not exist in patched object, Chimney will not be able to generate
Patcher.
Example
//> using dep io.scalaland::chimney::1.10.0
import io.scalaland.chimney.dsl._
case class User(id: Int, email: String, phone: Long)
case class UserUpdateForm(email: String, phone: Long, address: String)
val user = User(10, "abc@@domain.com", 1234567890L)
user.patchUsing(UserUpdateForm("xyz@@domain.com", 123123123L, "some address"))
// expected error:
// Chimney can't derive patching for User with patch type UserUpdateForm
//
// Field named 'address' not found in target patching type snippet.User!
//
// Consult https://chimney.readthedocs.io for usage examples.
This default behavior is intentional to prevent silent oversight of typos in patcher field names.
But there is a way to ignore redundant patcher fields explicitly with .ignoreRedundantPatcherFields operation.
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class User(id: Int, email: String, phone: Long)
case class UserUpdateForm(email: String, phone: Long, address: String)
val user = User(10, "abc@@domain.com", 1234567890L)
pprint.pprintln(
user
.using(UserUpdateForm("xyz@@domain.com", 123123123L, "some address"))
.ignoreRedundantPatcherFields
.patch
)
// expected output:
// User(id = 10, email = "xyz@@domain.com", phone = 123123123L)
locally {
// All patching derived in this scope will see these new flags (Scala 2-only syntax, see cookbook for Scala 3)
implicit val cfg = PatcherConfiguration.default.ignoreRedundantPatcherFields
pprint.pprintln(
user.patchUsing(UserUpdateForm("xyz@@domain.com", 123123123L, "some address"))
)
// expected output:
// User(id = 10, email = "xyz@@domain.com", phone = 123123123L)
}
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[User, UserUpdateForm] = Patcher.define[User, UserUpdateForm]
.ignoreRedundantPatcherFields
.buildPatcher
Patching succeeded using only relevant fields that appear in the patched object and ignoring address: String field
from the patch.
If the flag was enabled in the implicit config it can be disabled with .failRedundantPatcherFields.
Example
//> using dep io.scalaland::chimney::1.10.0
import io.scalaland.chimney.dsl._
case class User(id: Int, email: String, phone: Long)
case class UserUpdateForm(email: String, phone: Long, address: String)
val user = User(10, "abc@@domain.com", 1234567890L)
// All patching derived in this scope will see these new flags (Scala 2-only syntax, see cookbook for Scala 3)
implicit val cfg = PatcherConfiguration.default.ignoreRedundantPatcherFields
user
.using(UserUpdateForm("xyz@@domain.com", 123123123L, "some address"))
.failRedundantPatcherFields
.patch
// expected error:
// Chimney can't derive patching for User with patch type UserUpdateForm
//
// Field named 'address' not found in target patching type User!
//
// Consult https://chimney.readthedocs.io for usage examples.
Alternatively you can explicitly point to the fields that you want to ignore:
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class User(id: Int, email: String, phone: Long)
case class UserUpdateForm(email: String, phone: Long, address: String)
val user = User(10, "abc@@domain.com", 1234567890L)
pprint.pprintln(
user
.using(UserUpdateForm("xyz@@domain.com", 123123123L, "some address"))
.withFieldIgnored(_.address)
.patch
)
// expected output:
// User(id = 10, email = "xyz@@domain.com", phone = 123123123L)
Updating AnyVal with AnyVal
It is possible to update values containing AnyVals:
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class Foo[A](value: A)
case class Bar[A](value: A)
case class Wrapper(str: String) extends AnyVal
pprint.pprintln(
Foo("aaa").patchUsing(Bar(Wrapper("bbb")))
)
// expected output:
// Foo(value = "bbb")
pprint.pprintln(
Foo(Wrapper("aaa")).patchUsing(Bar("bbb"))
)
// expected output:
// Foo(value = Wrapper(str = "bbb"))
pprint.pprintln(
Foo(Wrapper("aaa")).patchUsing(Bar(Wrapper("bbb")))
)
// expected output:
// Foo(value = Wrapper(str = "bbb"))
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[Foo[String], Bar[Wrapper]] = Patcher.derive[Foo[String], Bar[Wrapper]]
// or (if you want to pass overrides):
val patcher2: Patcher[Foo[String], Bar[Wrapper]] = Patcher.define[Foo[String], Bar[Wrapper]]
.buildPatcher
Updating value with Option
It is possible to patch using optional values of type Option[A] as long as the Transformer is available (or derivable)
for A. If the value is present (Some), it’s used for patching a field in the target object; otherwise (None) it’s ignored
and the field value is copied from the original object.
Let’s consider the following patch:
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class User(id: Int, email: String, phone: Long)
case class UserPatch(email: Option[String], phone: Option[Long])
val user = User(10, "abc@@domain.com", 1234567890L)
val update = UserPatch(email = Some("updated@@example.com"), phone = None)
pprint.pprintln(
user.patchUsing(update)
)
// expected output:
// User(id = 10, email = "updated@@example.com", phone = 1234567890L)
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[User, UserPatch] = Patcher.derive[User, UserPatch]
// or (if you want to pass overrides):
val patcher2: Patcher[User, UserPatch] = Patcher.define[User, UserPatch]
.buildPatcher
The field phone remained the same as in the original user, while the optional e-mail string got updated from
a patch object.
Tip
There are special rules which makes sure that we can not only update A with Option[B], but also
Option[A]withOption[Option[B]]Either[A, B]withOption[Either[C, D]]Collection1[A]withOption[Collection2[B]]
Treating None as no-update instead of "set to None"
An interesting case appears when both the patch case class and the patched object define fields f: Option[A].
Depending on the values of f in the patched object and patch, we would like to apply the following semantic table:
patchedObject.f |
patch.f |
patching result |
|---|---|---|
None |
Some(value) |
Some(value) |
Some(value1) |
Some(value2) |
Some(value2) |
None |
None |
None |
Some(value) |
None |
??? |
When a patch.f contains some value, it’s immediately used for replacing a field in the target object (rows 1 and 2),
regardless of the original object field value. When both field are None, the patching result is also None (row 3).
But if the original object contains a some value, but the patch comes with a None, we can do two things:
- clear value in target object (replace it with
None) - or ignore updating this particular field (as in the previous section)
Both choices may make perfect sense, depending on the context. By default, Chimney does the former (clears the value),
but it also gives a simple way to always ignore None from the patch with .ignoreNoneInPatch operation.
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class User(name: Option[String], age: Option[Int])
case class UserPatch(name: Option[String], age: Option[Int])
val user = User(Some("John"), Some(30))
val userPatch = UserPatch(None, None)
pprint.pprintln(
user.patchUsing(userPatch)
)
// clears both fields:
// expected output:
// User(name = None, age = None)
pprint.pprintln(
user
.using(userPatch)
.ignoreNoneInPatch
.patch
)
// ignores updating both fields:
// expected output:
// User(name = Some(value = "John"), age = Some(value = 30))
locally {
// All patching derived in this scope will see these new flags (Scala 2-only syntax, see cookbook for Scala 3)
implicit val cfg = PatcherConfiguration.default.ignoreNoneInPatch
pprint.pprintln(
user.patchUsing(userPatch)
)
// ignores updating both fields:
// expected output:
// User(name = Some(value = "John"), age = Some(value = 30))
}
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[User, UserPatch] = Patcher.define[User, UserPatch]
.ignoreNoneInPatch
.buildPatcher
If the flag was enabled in the implicit config it can be disabled with .clearOnNoneInPatch.
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class User(name: Option[String], age: Option[Int])
case class UserPatch(name: Option[String], age: Option[Int])
val user = User(Some("John"), Some(30))
val userPatch = UserPatch(None, None)
// all patching derived in this scope will see these new flags
implicit val cfg = PatcherConfiguration.default.ignoreNoneInPatch
pprint.pprintln(
user
.using(userPatch)
.clearOnNoneInPatch
.patch
)
// clears both fields:
// expected output:
// User(name = None, age = None)
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[User, UserPatch] = Patcher.define[User, UserPatch]
.clearOnNoneInPatch
.buildPatcher
Unambiguous Option update
The flag is not required when updating Option[A] with Option[Option[B]], as then it is always
unambiguous what to do:
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class User(name: Option[String], age: Option[Int])
case class UserPatch(name: Option[Option[String]], age: Option[Option[Int]])
val user = User(Some("John"), Some(30))
val userPatch1 = UserPatch(None, None)
pprint.pprintln(
user.patchUsing(userPatch1)
)
// clears both fields:
// expected output:
// User(name = Some(value = "John"), age = Some(value = 30))
val userPatch2 = UserPatch(Some(Some("Jane")), Some(Some(25)))
pprint.pprintln(
user.patchUsing(userPatch2)
)
// ignores updating both fields:
// expected output:
// User(name = Some(value = "Jane"), age = Some(value = 25))
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[User, UserPatch] = Patcher.derive[User, UserPatch]
// or (if you want to pass overrides):
val patcher2: Patcher[User, UserPatch] = Patcher.define[User, UserPatch]
.buildPatcher
Updating value with Either
By default patch always just replaces the old value with a new one:
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class User(name: Either[String, String], age: Either[String, Int])
case class UserPatch(name: Either[String, String], age: Either[String, Int])
val user = User(Right("John"), Right(30))
val userPatch = UserPatch(Left("nope"), Left("nope"))
pprint.pprintln(
user.patchUsing(userPatch)
)
// expected output:
// User(name = Left(value = "nope"), age = Left(value = "nope"))
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[User, UserPatch] = Patcher.derive[User, UserPatch]
// or (if you want to pass overrides):
val patcher2: Patcher[User, UserPatch] = Patcher.define[User, UserPatch]
.buildPatcher
Treating Left as no-update instead of "set to Left"
Depending on the values of f in the patched object and patch, we could like to apply the following semantic table:
patchedObject.f |
patch.f |
patching result |
|---|---|---|
Left(err) |
Right(value) |
Right(value) |
Right(value1) |
Right(value2) |
Right(value2) |
Left(err1) |
Left(err2) |
Left(err2) |
Right(value) |
Left(err) |
??? |
When a patch.f contains Right value, it’s immediately used for replacing a field in the target object (rows 1 and 2),
regardless of the original object field value. When both field are Left, the patching result is also Left
(with newer error) (row 3).
But if the original object contains a Right value, but the patch comes with a Left, we can do two things:
- fail value in target object (replace it with
Left) - or ignore updating this particular field (as in the previous section)
Both choices may make perfect sense, depending on the context. By default, Chimney does the former (fails the value),
but it also gives a simple way to always ignore Left from the patch with .ignoreLeftInPatch operation.
The latter would assume that Either is Right-biased.
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class User(name: Either[String, String], age: Either[String, Int])
case class UserPatch(name: Either[String, String], age: Either[String, Int])
val user = User(Right("John"), Right(30))
val userPatch = UserPatch(Left("nope"), Left("nope"))
pprint.pprintln(
user.patchUsing(userPatch)
)
// fails both fields:
// expected output:
// User(name = Left(value = "nope"), age = Left(value = "nope"))
pprint.pprintln(
user
.using(userPatch)
.ignoreLeftInPatch
.patch
)
// ignores updating both fields:
// expected output:
// User(name = Right(value = "John"), age = Right(value = 30))
locally {
// All patching derived in this scope will see these new flags (Scala 2-only syntax, see cookbook for Scala 3)
implicit val cfg = PatcherConfiguration.default.ignoreLeftInPatch
pprint.pprintln(
user.patchUsing(userPatch)
)
// ignores updating both fields:
// expected output:
// User(name = Right(value = "John"), age = Right(value = 30))
}
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher2: Patcher[User, UserPatch] = Patcher.define[User, UserPatch]
.ignoreLeftInPatch
.buildPatcher
If the flag was enabled in the implicit config it can be disabled with .useLeftOnLeftInPatch.
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class User(name: Either[String, String], age: Either[String, Int])
case class UserPatch(name: Either[String, String], age: Either[String, Int])
val user = User(Right("John"), Right(30))
val userPatch = UserPatch(Left("nope"), Left("nope"))
// all patching derived in this scope will see these new flags
implicit val cfg = PatcherConfiguration.default.ignoreLeftInPatch
pprint.pprintln(
user
.using(userPatch)
.useLeftOnLeftInPatch
.patch
)
// clears both fields:
// expected output:
// User(name = Left(value = "nope"), age = Left(value = "nope"))
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher2: Patcher[User, UserPatch] = Patcher.define[User, UserPatch]
.useLeftOnLeftInPatch
.buildPatcher
Unambiguous Either update
The flag is not required when updating Either[K, V] with Option[Either[K2, V2]], as then it is always
unambiguous what to do:
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class User(name: Either[String, String], age: Either[String, Int])
case class UserPatch(name: Option[Either[String, String]], age: Option[Either[String, Int]])
val user = User(Right("John"), Right(30))
val userPatch1 = UserPatch(None, None)
pprint.pprintln(
user.patchUsing(userPatch1)
)
// clears both fields:
// expected output:
// User(name = Right(value = "John"), age = Right(value = 30))
val userPatch2 = UserPatch(Some(Right("Jane")), Some(Right(25)))
pprint.pprintln(
user.patchUsing(userPatch2)
)
// ignores updating both fields:
// expected output:
// User(name = Right(value = "Jane"), age = Right(value = 25))
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[User, UserPatch] = Patcher.derive[User, UserPatch]
// or (if you want to pass overrides):
val patcher2: Patcher[User, UserPatch] = Patcher.define[User, UserPatch]
.buildPatcher
Updating value with collection
By default patch always just replaces the old value with a new one:
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class UserStats(names: List[String], ages: List[Int])
case class UserStatsPatch(names: Vector[String], ages: Vector[Int])
val user = UserStats(List("John"), List(30))
val userPatch = UserStatsPatch(Vector("Jane"), Vector(25))
pprint.pprintln(
user.patchUsing(userPatch)
)
// expected output:
// UserStats(names = List("Jane"), ages = List(25))
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[UserStats, UserStatsPatch] = Patcher.derive[UserStats, UserStatsPatch]
// or (if you want to pass overrides):
val patcher2: Patcher[UserStats, UserStatsPatch] = Patcher.define[UserStats, UserStatsPatch]
.buildPatcher
Appending to collection instead of replacing it
If the original object contains a collection, and the patch comes with another one`, we can do two things:
- replace the old one with a new one
- or append the value from patch to the existing value
Both choices may make perfect sense, depending on the context. By default, Chimney does the former (replaces the value), but it also gives a simple way to append collection to the old value.
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class UserStats(names: List[String], ages: List[Int])
case class UserStatsPatch(names: Vector[String], ages: Vector[Int])
val user = UserStats(List("John"), List(30))
val userPatch = UserStatsPatch(Vector("Jane"), Vector(25))
pprint.pprintln(
user.patchUsing(userPatch)
)
// fails both fields:
// expected output:
// UserStats(names = List("Jane"), ages = List(25))
pprint.pprintln(
user
.using(userPatch)
.appendCollectionInPatch
.patch
)
// ignores updating both fields:
// expected output:
// UserStats(names = List("John", "Jane"), ages = List(30, 25))
locally {
// All patching derived in this scope will see these new flags (Scala 2-only syntax, see cookbook for Scala 3)
implicit val cfg = PatcherConfiguration.default.appendCollectionInPatch
pprint.pprintln(
user.patchUsing(userPatch)
)
// ignores updating both fields:
// expected output:
// UserStats(names = List("John", "Jane"), ages = List(30, 25))
}
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[UserStats, UserStatsPatch] = Patcher.define[UserStats, UserStatsPatch]
.appendCollectionInPatch
.buildPatcher
If the flag was enabled in the implicit config it can be disabled with .overrideCollectionInPatch.
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class UserStats(names: List[String], ages: List[Int])
case class UserStatsPatch(names: Vector[String], ages: Vector[Int])
val user = UserStats(List("John"), List(30))
val userPatch = UserStatsPatch(Vector("Jane"), Vector(25))
// all patching derived in this scope will see these new flags
implicit val cfg = PatcherConfiguration.default.appendCollectionInPatch
pprint.pprintln(
user
.using(userPatch)
.overrideCollectionInPatch
.patch
)
// clears both fields:
// expected output:
// UserStats(names = List("Jane"), ages = List(25))
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[UserStats, UserStatsPatch] = Patcher.define[UserStats, UserStatsPatch]
.overrideCollectionInPatch
.buildPatcher
Unambiguous collection update
The flag is not required when updating collection with Option[collection], as then it is always
unambiguous what to do (leave unchanged or replace):
Example
//> using dep io.scalaland::chimney::1.10.0
//> using dep com.lihaoyi::pprint::0.9.0
import io.scalaland.chimney.dsl._
case class UserStats(names: List[String], ages: List[Int])
case class UserStatsPatch(names: Option[Vector[String]], ages: Option[Vector[Int]])
val user = UserStats(List("John"), List(30))
val userPatch1 = UserStatsPatch(None, None)
pprint.pprintln(
user.patchUsing(userPatch1)
)
// clears both fields:
// expected output:
// UserStats(names = List("John"), ages = List(30))
val userPatch2 = UserStatsPatch(Some(Vector("Jane")), Some(Vector(25)))
pprint.pprintln(
user.patchUsing(userPatch2)
)
// ignores updating both fields:
// expected output:
// UserStats(names = List("Jane"), ages = List(25))
import io.scalaland.chimney.Patcher
// If we want to reuse Patcher, we can create implicits using:
val patcher: Patcher[UserStats, UserStatsPatch] = Patcher.derive[UserStats, UserStatsPatch]
// or (if you want to pass overrides):
val patcher2: Patcher[UserStats, UserStatsPatch] = Patcher.define[UserStats, UserStatsPatch]
.buildPatcher