|
1 | 1 | 
|
2 | 2 |
|
3 |
| -[](https://travis-ci.org/softwaremill/diffx) |
| 3 | +[](https://github.com/softwaremill/diffx/actions) |
4 | 4 | [](https://search.maven.org/search?q=g:com.softwaremill.diffx)
|
5 | 5 | [](https://gitter.im/softwaremill/diffx?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
6 | 6 | [](https://mergify.io)
|
7 | 7 | [](https://scala-steward.org)
|
8 | 8 |
|
9 |
| -Pretty diffs for case classes. |
| 9 | +Pretty diffs for case classes. |
10 | 10 |
|
11 |
| -The library is published for Scala 2.12 and 2.13. |
| 11 | +## Documentation |
12 | 12 |
|
13 |
| -## Table of contents |
14 |
| -- [goals of the project](#goals-of-the-project) |
15 |
| -- [teaser](#teaser) |
16 |
| -- [derivation](#derivation) |
17 |
| -- [colors](#colors) |
18 |
| -- integrations |
19 |
| - - [scalatest](#scalatest-integration) |
20 |
| - - [specs2](#specs2-integration) |
21 |
| - - [utest](#utest-integration) |
22 |
| - - [other](#other-3rd-party-libraries-support) |
23 |
| -- [ignoring](#ignoring) |
24 |
| -- [customization](#customization) |
25 |
| -- [similar projects](#similar-projects) |
26 |
| -- [commercial support](#commercial-support) |
| 13 | +diffx documentation is available at [diffx-scala.readthedocs.io](https://diffx-scala.readthedocs.io). |
27 | 14 |
|
28 |
| -## Goals of the project |
| 15 | +## Modifying documentation |
| 16 | +The documentation is typechecked using `mdoc`. The sources for the documentation exist in `docs-sources`. Don't modify the generated documentation in `generated-docs`, as these files will get overwritten! |
29 | 17 |
|
30 |
| -- human-readable case class diffs |
31 |
| -- support for popular testing frameworks |
32 |
| -- OOTB collections support |
33 |
| -- OOTB non-case class support |
34 |
| -- smaller compilation overhead compared to shapless based solutions (thanks to magnolia <3) |
35 |
| -- programmer friendly and type safe api for partial ignore |
| 18 | +When generating documentation, it's best to set the version to the current one, so that the generated doc files don't include modifications with the current snapshot version. |
36 | 19 |
|
37 |
| -## Teaser |
38 |
| -Add the following dependency: |
39 |
| - |
40 |
| -```scala |
41 |
| -"com.softwaremill.diffx" %% "diffx-core" % "0.5.0" |
42 |
| -``` |
43 |
| - |
44 |
| -```scala |
45 |
| -sealed trait Parent |
46 |
| -case class Bar(s: String, i: Int) extends Parent |
47 |
| -case class Foo(bar: Bar, b: List[Int], parent: Option[Parent]) extends Parent |
48 |
| - |
49 |
| -val right: Foo = Foo( |
50 |
| - Bar("asdf", 5), |
51 |
| - List(123, 1234), |
52 |
| - Some(Bar("asdf", 5)) |
53 |
| -) |
54 |
| -// right: Foo = Foo( |
55 |
| -// bar = Bar(s = "asdf", i = 5), |
56 |
| -// b = List(123, 1234), |
57 |
| -// parent = Some(value = Bar(s = "asdf", i = 5)) |
58 |
| -// ) |
59 |
| - |
60 |
| -val left: Foo = Foo( |
61 |
| - Bar("asdf", 66), |
62 |
| - List(1234), |
63 |
| - Some(right) |
64 |
| -) |
65 |
| -// left: Foo = Foo( |
66 |
| -// bar = Bar(s = "asdf", i = 66), |
67 |
| -// b = List(1234), |
68 |
| -// parent = Some( |
69 |
| -// value = Foo( |
70 |
| -// bar = Bar(s = "asdf", i = 5), |
71 |
| -// b = List(123, 1234), |
72 |
| -// parent = Some(value = Bar(s = "asdf", i = 5)) |
73 |
| -// ) |
74 |
| -// ) |
75 |
| -// ) |
76 |
| - |
77 |
| -import com.softwaremill.diffx.generic.auto._ |
78 |
| -import com.softwaremill.diffx._ |
79 |
| -compare(left, right) |
80 |
| -// res0: DiffResult = DiffResultObject( |
81 |
| -// name = "Foo", |
82 |
| -// fields = ListMap( |
83 |
| -// "bar" -> DiffResultObject( |
84 |
| -// name = "Bar", |
85 |
| -// fields = ListMap( |
86 |
| -// "s" -> Identical(value = "asdf"), |
87 |
| -// "i" -> DiffResultValue(left = 66, right = 5) |
88 |
| -// ) |
89 |
| -// ), |
90 |
| -// "b" -> DiffResultObject( |
91 |
| -// name = "List", |
92 |
| -// fields = ListMap( |
93 |
| -// "0" -> DiffResultValue(left = 1234, right = 123), |
94 |
| -// "1" -> DiffResultMissing(value = 1234) |
95 |
| -// ) |
96 |
| -// ), |
97 |
| -// "parent" -> DiffResultValue( |
98 |
| -// left = "repl.MdocSession.App.Foo", |
99 |
| -// right = "repl.MdocSession.App.Bar" |
100 |
| -// ) |
101 |
| -// ) |
102 |
| -// ) |
103 |
| -``` |
104 |
| - |
105 |
| -Will result in: |
106 |
| - |
107 |
| - |
108 |
| - |
109 |
| - |
110 |
| -## Derivation |
111 |
| - |
112 |
| -Diffx supports auto and semi-auto derivation. |
113 |
| - |
114 |
| -For semi-auto derivation you don't need any additional import, just define your instances using: |
115 |
| -```scala |
116 |
| -case class Product(name: String) |
117 |
| -case class Basket(products: List[Product]) |
118 |
| - |
119 |
| -implicit val productDiff = Diff.derived[Product] |
120 |
| -implicit val basketDiff = Diff.derived[Basket] |
121 |
| -``` |
122 |
| - |
123 |
| -To use auto derivation add following import |
124 |
| - |
125 |
| -`import com.softwaremill.diffx.generic.auto._` |
126 |
| - |
127 |
| -or |
128 |
| - |
129 |
| -extend trait |
130 |
| - |
131 |
| -`com.softwaremill.diffx.generic.DiffDerivation` |
132 |
| - |
133 |
| -**Auto derivation will have a huge impact on compilation times**, because of that it is recommended to use `semi-auto` derivation. |
134 |
| - |
135 |
| - |
136 |
| -## Colors |
137 |
| - |
138 |
| -When running tests through sbt, default diffx's colors work well on both dark and light backgrounds. |
139 |
| -Unfortunately Intellij Idea forces the default color to red when displaying test's error. |
140 |
| -This means that it is impossible to print something with the standard default color (either white or black depending on the color scheme). |
141 |
| - |
142 |
| -To have better colors, external information about the desired theme is required. |
143 |
| -Specify environment variable `DIFFX_COLOR_THEME` and set it to either `light` or `dark`. |
144 |
| -I had to specify it in `/etc/environment` rather than home profile for Intellij Idea to picked it up. |
145 |
| - |
146 |
| -If anyone has an idea how this could be improved, I am open for suggestions. |
147 |
| - |
148 |
| -## Scalatest integration |
149 |
| - |
150 |
| -To use with scalatest, add the following dependency: |
151 |
| - |
152 |
| -```scala |
153 |
| -"com.softwaremill.diffx" %% "diffx-scalatest" % "0.5.0" % Test |
154 |
| -``` |
155 |
| - |
156 |
| -Then, extend the `com.softwaremill.diffx.scalatest.DiffMatcher` trait or `import com.softwaremill.diffx.scalatest.DiffMatcher._`. |
157 |
| -After that you will be able to use syntax such as: |
158 |
| - |
159 |
| -```scala |
160 |
| -import org.scalatest.matchers.should.Matchers._ |
161 |
| -import com.softwaremill.diffx.scalatest.DiffMatcher._ |
162 |
| - |
163 |
| -left should matchTo(right) |
164 |
| -``` |
165 |
| - |
166 |
| -Giving you nice error messages: |
167 |
| - |
168 |
| -## Specs2 integration |
169 |
| - |
170 |
| -To use with specs2, add the following dependency: |
171 |
| - |
172 |
| -```scala |
173 |
| -"com.softwaremill.diffx" %% "diffx-specs2" % "0.5.0" % Test |
174 |
| -``` |
175 |
| - |
176 |
| -Then, extend the `com.softwaremill.diffx.specs2.DiffMatcher` trait or `import com.softwaremill.diffx.specs2.DiffMatcher._`. |
177 |
| -After that you will be able to use syntax such as: |
178 |
| - |
179 |
| -```scala |
180 |
| -import org.specs2.matcher.MustMatchers.{left => _, right => _, _} |
181 |
| -import com.softwaremill.diffx.specs2.DiffMatcher._ |
182 |
| - |
183 |
| -left must matchTo(right) |
184 |
| -``` |
185 |
| - |
186 |
| -## Utest integration |
187 |
| - |
188 |
| -To use with utest, add following dependency: |
189 |
| - |
190 |
| -```scala |
191 |
| -"com.softwaremill.diffx" %% "diffx-utest" % "0.5.0" % Test |
192 |
| -``` |
193 |
| - |
194 |
| -Then, mixin `DiffxAssertions` trait or add `import com.softwaremill.diffx.utest.DiffxAssertions._` to your test code. |
195 |
| -To assert using diffx use `assertEquals` as follows: |
196 |
| - |
197 |
| -```scala |
198 |
| -import com.softwaremill.diffx.utest.DiffxAssertions._ |
199 |
| -assertEqual(left, right) |
200 |
| -``` |
201 |
| - |
202 |
| -## Ignoring |
203 |
| - |
204 |
| -Fields can be excluded from comparision by calling the `ignore` method on the `Diff` instance. |
205 |
| -Since `Diff` instances are immutable, the `ignore` method creates a copy of the instance with modified logic. |
206 |
| -You can use this instance explicitly. |
207 |
| -If you still would like to use it implicitly, you first need to summon the instance of the `Diff` typeclass using |
208 |
| -the `Derived` typeclass wrapper: `Derived[Diff[Person]]`. Thanks to that trick, later you will be able to put your modified |
209 |
| -instance of the `Diff` typeclass into the implicit scope. The whole process looks as follows: |
210 |
| - |
211 |
| -```scala |
212 |
| -case class Person(name:String, age:Int) |
213 |
| -implicit val modifiedDiff: Diff[Person] = Derived[Diff[Person]].ignore(_.name) |
214 |
| -``` |
215 |
| - |
216 |
| -## Customization |
217 |
| - |
218 |
| -If you'd like to implement custom matching logic for the given type, create an implicit `Diff` instance for that |
219 |
| -type, and make sure it's in scope when any `Diff` instances depending on that type are created. |
220 |
| - |
221 |
| -Consider following example with `NonEmptyList` from cats. `NonEmptyList` is implemented as case class so diffx |
222 |
| -will create a `Diff[NonEmptyList]` typeclass instance using magnolia derivation. |
223 |
| - |
224 |
| -Obviously that's not what we usually want. In most of the cases we would like `NonEmptyList` to be compared as a list. |
225 |
| -Diffx already has an instance of a typeclass for a list. One more thing to do is to use that typeclass by converting `NonEmptyList` to list which can be done using `contramap` method. |
226 |
| - |
227 |
| -The final code looks as follows: |
228 |
| - |
229 |
| -```scala |
230 |
| -import cats.data.NonEmptyList |
231 |
| -implicit def nelDiff[T: Diff]: Diff[NonEmptyList[T]] = |
232 |
| - Diff[List[T]].contramap[NonEmptyList[T]](_.toList) |
233 |
| -``` |
234 |
| - |
235 |
| -And here's an example of customizing the `Diff` instance for a child class of a sealed trait |
236 |
| - |
237 |
| -```scala |
238 |
| -sealed trait ABParent |
239 |
| -case class A(id: String, name: String) extends ABParent |
240 |
| -case class B(id: String, name: String) extends ABParent |
241 |
| - |
242 |
| -implicit val diffA: Diff[A] = Derived[Diff[A]].ignore(_.id) |
243 |
| -``` |
244 |
| -```scala |
245 |
| -val a1: ABParent = A("1", "X") |
246 |
| -// a1: ABParent = A(id = "1", name = "X") |
247 |
| -val a2: ABParent = A("2", "X") |
248 |
| -// a2: ABParent = A(id = "2", name = "X") |
249 |
| - |
250 |
| -compare(a1, a2) |
251 |
| -// res6: DiffResult = Identical(value = A(id = "1", name = "X")) |
252 |
| -``` |
253 |
| - |
254 |
| -As you can see instead of summoning bare instance of `Diff` for given `A` we summoned `Derived[Diff[A]]`. |
255 |
| -This is required in order to workaround self reference error. |
256 |
| - |
257 |
| -You may need to add `-Wmacros:after` Scala compiler option to make sure to check for unused implicits |
258 |
| -after macro expansion. |
259 |
| -If you get warnings from Magnolia which looks like `magnolia: using fallback derivation for TYPE`, |
260 |
| -you can use the [Silencer](https://github.com/ghik/silencer) compiler plugin to silent the warning |
261 |
| -with the compiler option `"-P:silencer:globalFilters=^magnolia: using fallback derivation.*$"` |
262 |
| - |
263 |
| -## Other 3rd party libraries support |
264 |
| - |
265 |
| -- [com.softwaremill.common.tagging](https://github.com/softwaremill/scala-common) |
266 |
| - ```scala |
267 |
| - "com.softwaremill.diffx" %% "diffx-tagging" % "0.5.0" |
268 |
| - ``` |
269 |
| - `com.softwaremill.diffx.tagging.DiffTaggingSupport` |
270 |
| -- [eu.timepit.refined](https://github.com/fthomas/refined) |
271 |
| - ```scala |
272 |
| - "com.softwaremill.diffx" %% "diffx-refined" % "0.5.0" |
273 |
| - ``` |
274 |
| - `com.softwaremill.diffx.refined.RefinedSupport` |
275 |
| -- [org.typelevel.cats](https://github.com/typelevel/cats) |
276 |
| - ```scala |
277 |
| - "com.softwaremill.diffx" %% "diffx-cats" % "0.5.0" |
278 |
| - ``` |
279 |
| - `com.softwaremill.diffx.cats.DiffCatsInstances` |
280 |
| - |
281 |
| -## Similar projects |
282 |
| - |
283 |
| -There is a number of similar projects from which diffx draws inspiration. |
284 |
| - |
285 |
| -Below is a list of some of them, which I am aware of, with their main differences: |
286 |
| -- [xotai/diff](https://github.com/xdotai/diff) - based on shapeless, seems not to be activly developed anymore |
287 |
| -- [ratatool-diffy](https://github.com/spotify/ratatool/tree/master/ratatool-diffy) - the main purpose is to compare large data sets stored on gs or hdfs |
288 |
| - |
289 |
| -## Commercial Support |
290 |
| - |
291 |
| -We offer commercial support for diffx and related technologies, as well as development services. [Contact us](https://softwaremill.com) to learn more about our offer! |
| 20 | +That is, in sbt run: `set version := "0.5.0"`, before running `mdoc` in `docs`. |
292 | 21 |
|
293 | 22 | ## Copyright
|
294 | 23 |
|
|
0 commit comments