circe 0.7.0

@travisbrown travisbrown released this Jan 17, 2017 · 22 commits to master since this release

This release includes the changes from the first and second 0.7.0 milestones, as well as a few other things:

  • Version updates: Cats (from 0.8.1 to 0.9.0), Monocle (to 1.4.0), and Refined (from 0.6.0 to 0.6.2).
  • A new TimeInstances trait for java.time instances (#545; thanks Sergey Kolbasov).
  • Printing optimizations and ByteBuffer printing support for Scala.js (#544).

Cumulatively the changes between 0.6 and 0.7 are pretty large, and the discussion of updating in the M1 release notes is likely to be particularly useful for people who are working with cursors directly. If you run into any questions or issues please get in touch on Gitter.

Thanks to all contributors (19+ since 0.6!), reviewers, and feedback-givers, and especially to @jonas and @n4to4 for all their work on the project site, documentation, and build configuration since 0.6.

Downloads

circe 0.7.0-M2

@travisbrown travisbrown released this Jan 10, 2017 · 41 commits to master since this release

This milestone includes all features that are planned for 0.7.0—the only thing we're waiting on is the Cats 0.9.0 release (tracked here) and some more feedback from people who have tried out the milestones.

Printing to byte buffers

On the JVM, JSON printers now have a prettyByteString method that allows printing a Json value directly to a byte buffer (#535 and #537). This addition is designed to support "string-less encoding" in Finch, and will be accompanied by an equivalent method in circe-jackson.

Thanks to Vladimir Kostyukov and @ImLiar for driving and helping with these ByteBuffer-related additions.

Kicking some modules out of the nest

Three modules have been moved to their own repositories in the circe organization:

Among other things, these moves have allowed us to simplify the build by scrapping sbt-doge and to publish circe-jackson artifacts for multiple Jackson versions.

This does mean that there will be a delay between the publication of the modules in this repository and circe-spray and circe-jackson, but we'll aim to keep it small.

Thanks especially to Jens Raaby and @n4to4 for helping with these transitions.

New cursor operation

ACursor now has a values method that's roughly equivalent to fields, but for JSON arrays instead of JSON objects (#535). This gap has come up a few times in conversations on Gitter and elsewhere—thanks to Eric Torreborre and others for finally convincing me that we should have it.

More concise circe-optics indexing

In circe-optics, JsonPath (and its Fold and Traversal cousins) now have apply and applyDynamic methods that support more concise JSON array indexing. For example, instead of root.users.index(1) you can now write root.users(1), and just root(1) instead of root.index(1).

Thanks to Paul Brimicombe for proposing and implementing these changes (#497 and #506).

Traits for generic derivers

Both circe-generic and circe-generic-extras now provide an AutoDerivation trait that can be mixed into an object that provides other instances. For example, instead of this:

class MyInstances { /* some type class instances here */ }
object MyInstances extends MyInstances

import MyInstances._
import io.circe.generic.auto._

You could write this:

import io.circe.generic.AutoDerivation

class MyInstances { /* some type class instances here */ }
object MyInstances extends MyInstances with AutoDerivation

import MyInstances._

Thanks to Michael Ledin for proposing and implementing this addition (#504).

De-predef-ication

The project now uses scalac's -Yno-predef option to remove the implicit import scala.Predef._ that Scala source files have by default. This makes the codebase a little more explicit and makes the introduction of operations supported by implicit conversions (which often involve undesirable allocations) easier to notice and avoid.

Thanks to @n4to4 for pushing this effort (started in #320 and #383) over the finish line (#526).

Truncation actually truncates

Previously the truncateToX methods on JsonNumber rounded to the nearest whole number:

scala> import io.circe.{ Json, JsonNumber }
import io.circe.{Json, JsonNumber}

scala> JsonNumber.fromString("1.5").map(_.truncateToLong)
res0: Option[Long] = Some(2)

scala> Json.fromDouble(1.5).flatMap(_.asNumber).map(_.truncateToLong)
res1: Option[Long] = Some(2)

scala> Json.fromBigDecimal(BigDecimal(1.5)).asNumber.map(_.truncateToLong)
res2: Option[Long] = Some(2)

This contradicted the API documentation for these methods:

Truncation means that we round toward zero to the closest valid scala.Long.

This release fixes the discrepancy by rounding toward zero (as specified in the docs) instead of to the nearest whole value (#533).

Other API changes

  • The new unsafeFilter method for circe-optics's JsonPath that was introduced in 0.7.0-M1 has been renamed to filterUnsafe for the sake of consistency (#490).
  • ArrayEncoder now has a contravariant functor instance (#491).

Other optimizations

  • Optimizations for printing (#535).
  • Minor optimizations for ACursor#downN and tuple decoders (#492).
  • Minor optimizations for circe-numbers and JsonNumber (#494).
  • Syntactic simplification in Decoder (#496).
  • String formatting optimizations (#534).

Other changes to versions, build configuration, and documentation

  • The Scala 2.12 version is now 2.12.1 (#493).
  • The Scala.js version is now 0.6.14 (#527).
  • The Monocle version for circe-optics is updated to 1.4.0-M2 (#532).
  • Build configuration simplifications (#534).
  • Adopted sbt-travisci to avoid duplicating Scala version numbers in build configuration (#499).
  • The project no longer uses sbt-doge (#519).
  • Miscellaneous documentation and metadata improvements (#489, #500, #507, #511, #515, #516, #517, #520).

Thanks to everyone who contributed code or documentation or who offered feedback!

Downloads

circe 0.7.0-M1

@travisbrown travisbrown released this Dec 2, 2016 · 132 commits to master since this release

This milestone release is intended as a preview of changes that will be introduced in 0.7.0 and should not be used in production or any other situation where you need things to be correct! The tests pass but there are fairly major internal changes here, and there may be more before 0.7.0 is released (which I'm hoping will happen by the end of next week). So please try out this milestone, but stick with 0.6.1 where it matters for now.

Upgrading

The biggest change in this release is to the representation of cursor and cursor operations (see #459 for the details). Both have been flattened: Cursor and HistoryOp are gone entirely, and HCursor now extends ACursor instead of being wrapped by it.

Many users are likely to be completely unaffected by these rearrangements. The two most common changes that will be necessary are related to the focus and acursor methods on HCursor. The focus method on HCursor now returns an Option[Json] instead of simply Json. If you have a cursor that's statically typed as an HCursor, you can now call value to get access to the Json value at the focus directly. If you're calling c.acursor on a c: HCursor, you can just remove the method call, since the HCursor is already an ACursor.

Vector also now replaces List in all API methods related to JSON arrays. You can simply call toVector or toList as necessary to fit your existing code, or you can rework your existing code to use vectors instead of lists.

If you're currently working with HistoryOp or Cursor values directly, the upgrade will be a little more work. As always, please ask a question on Gitter or Stack Overflow if you run into problems.

Why are you doing this to us?

These are big changes, but I think they're worthwhile. For one thing, the pull request introducing the cursor rewrite nets 397 deletions, and the API is a couple of unnecessary types lighter. These changes also significantly improve decoding performance. Here are before-and-after results, with Argonaut and Spray included for comparison:

Benchmark                               Mode  Cnt      Score      Error  Units
DecodingBenchmark.decodeFoosC (0.6.0)  thrpt   20   4507.107 ±   34.709  ops/s
DecodingBenchmark.decodeFoosC (new)    thrpt   20   8905.988 ±  135.244  ops/s
DecodingBenchmark.decodeFoosA          thrpt   20   1493.519 ±   35.113  ops/s
DecodingBenchmark.decodeFoosS          thrpt   20   9573.566 ±   43.642  ops/s

DecodingBenchmark.decodeIntsC (0.6.0)  thrpt   20  21813.721 ±  202.030  ops/s
DecodingBenchmark.decodeIntsC (new)    thrpt   20  49015.320 ± 3502.542  ops/s
DecodingBenchmark.decodeIntsA          thrpt   20   9097.675 ±   57.517  ops/s
DecodingBenchmark.decodeIntsS          thrpt   20  37520.796 ± 1154.570  ops/s

Most applications won't suddenly be twice as fast, of course (in the city lots tutorial project this only translates into about a 15% improvement in speed, for example), but it's still a pretty big boost, and together with the simplifications I think it's worth it.

Other changes

There are a few other changes since 0.6.1:

  • Bazillions of negative numbers that nobody cares about can now be decoded as BigInts (#482).
  • ObjectEncoder now has a Contravariant instance (thanks to @backuitist in #472).
  • arrFilter and objFilter in circe-optics have now been renamed (to filterByIndex and filterByField), and new filter and unsafeFilter methods have been introduced (thanks @julien-truffaut, #473).
  • truncateToLong now returns correct results for large values (#480).
  • JSON string values are now parsed as JSON numbers during decoding to integral types, instead of using Scala's toInt, etc. (#485).

The project has also moved from my personal GitHub account to a brand new circe organization—thanks to @GitHub for helping to make this happen.

Lastly, special thanks go to @jonas for simplifying the build (#474), moving us to @47deg's sbt-microsites (#462), and making many other much-needed documentation improvements.

Downloads

circe 0.6.1

@travisbrown travisbrown released this Nov 20, 2016 · 207 commits to master since this release

This release fixes a bug that caused HCursor#history to stack overflow when the history was too long (#458). It also includes some small performance improvements (#457), updates Jawn to 0.10.4 (#449), and follows Cats conventions for POM metadata (#453).

Downloads

circe 0.6.0

@travisbrown travisbrown released this Nov 10, 2016 · 232 commits to master since this release

For a detailed list of the changes and new features in this release, please see the 0.6.0-RC1 release notes.

The 0.6.0 release is the same as the release candidate with a few additions, version updates, and some cosmetic renaming:

  • We're now publishing artifacts for Scala 2.12 (in addition to 2.10 and 2.11, but not 2.12.0-RC2).
  • The patch versions for Cats, Jawn, Monocle, and ScalaCheck are all updated.
  • The new circe-shapeless module has been renamed to circe-shapes (#443).
  • The circe-shapes module now includes coproduct instances, and some HList instances have been renamed for consistency with the coproduct ones (#441).
  • Decoder has new decodeList and decodeVector methods, and Encoder has encodeList, encodeVector, and encodeSet (#442).

Thanks again to all contributors and to everyone who offered feedback on 0.6.0-RC1.

Downloads

Pre-release

circe 0.6.0-RC1

@travisbrown travisbrown released this Nov 2, 2016 · 256 commits to master since this release

This 0.6.0 release candidate includes several big changes, a couple of new modules, and a handful of smaller fixes and additions.

Introducing Scala 2.12

We are now publishing artifacts for Scala 2.12.0-RC2. These are available for all circe modules except circe-spray (since Spray doesn't yet support Scala 2.12). Just update your Scala version and you're ready to go (#417).

Note that Scala 2.12 requires Java 8. The circe-optics module now also requires Java 8 on Scala 2.11, since we've updated to Monocle 1.3.1, which now has this requirement (for 2.11 only, not 2.10).

Why just a candidate?

The primary reason that this is a release candidate is that there are a lot of changes here, and I'd like people to be able to kick the tires (especially on the new generic-extras module) before we put the 0.6.0 stamp on it.

Another reason is that Cats is not yet available for 2.12.0 (only 2.12.0-RC2). This should change soon, and the circe 0.6.0 release will only support 2.12.0 (not 2.12.0-RC2).

Assuming no major issues turn up (and the Cats 2.12.0 artifacts are ready), I'll release 0.6.0 early next week.

No more Xor

Most of the changes in this release are source compatible—for example, the generic module has been overhauled, but 99.9% of normal usage is unaffected. There's one big exception, though: the decoding result type is now Either instead of Xor, which Cats removed in 0.8.0 (#424).

If you're on Scala 2.12, in many cases you should be able to upgrade by simply replacing Xor with Either throughout your code. If you're on 2.10 or 2.11, or if you make use of methods that were on the Xor companion object, you'll need to import Cats's Either machinery (see the Cats release notes for more details).

Generic derivation overhaul

This release includes major changes to the implementation and arrangement of the generic module (#429). For the most part this shouldn't require any changes in your code, with one exception: derived instances for Shapeless's generic representations (records and coproducts) are no longer directly available. For example, previously you could write this:

scala> import io.circe.generic.auto._, shapeless.record.Record
import io.circe.generic.auto._
import shapeless.record.Record

scala> case class Foo(s: String, i: Int)
defined class Foo

scala> io.circe.Decoder[Foo]
res0: io.circe.Decoder[Foo] = io.circe.generic.decoding.DerivedDecoder$$anon$1@555bf2d

scala> io.circe.Decoder[Record.`'s -> String, 'i -> Int`.T]
res1: io.circe.Decoder[shapeless.::[String with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("s")],String]...

And circe would happily provide instances for both Foo and its generic representation (the Record thing). Now you only get the instance for Foo.

I'm not aware of anyone actually using this functionality, but if you are, and you need it, skip ahead to the section below on the circe-shapeless module.

There's one other minor change (fixing an oversight): the static return type of the semi-automatic deriveEncoder is now ObjectEncoder instead of just Encoder (#422).

If you notice any other changes in the behavior of circe-generic between 0.5 and 0.6, I'm considering that a bug, and would appreciate a report.

The overhaul accomplishes a couple of things. Most importantly, it makes the new configurable generic derivation (see the next section) possible, with relatively little fragility and duplication. It also has a pretty substantial impact on compile times—compiling circe's own test suite drops from around a minute and fifteen seconds to just under a minute on my machine, and the resulting class files are almost 10% smaller.

New generic-extras module

Configurable generic derivation

I've been promising people that this was right around the corner since January, and it's finally here:

import io.circe.parser.decode, io.circe.syntax._
import io.circe.generic.extras.Configuration, io.circe.generic.extras.auto._

sealed trait Stuff
case class Foo(thisIsAString: String, anotherField: Int = 13) extends Stuff
case class Bar(stuff: Stuff) extends Stuff

implicit val customConfig: Configuration = 
  Configuration.default.withSnakeCaseKeys.withDefaults.withDiscriminator("type")

val doc = """{ "type": "Bar", "stuff": { "type": "Foo", "this_is_a_string": "abc" }}"""
val stuff: Stuff = Bar(Bar(Foo("xyz", 23)))

And then:

scala> decode[Stuff](doc)
res0: Either[io.circe.Error,Stuff] = Right(Bar(Foo(abc,13)))

scala> stuff.asJson
res2: io.circe.Json =
{
  "stuff" : {
    "stuff" : {
      "this_is_a_string" : "xyz",
      "another_field" : 23,
      "type" : "Foo"
    },
    "type" : "Bar"
  },
  "type" : "Bar"
}

You can even mix and match:

import io.circe.{ Decoder, ObjectEncoder }
import io.circe.parser.decode, io.circe.syntax._
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.auto._

import io.circe.generic.{ semiauto => boring }
import io.circe.generic.extras.{ semiauto => fancy }

implicit val customConfig: Configuration = 
  Configuration.default.withSnakeCaseKeys.withDefaults.withDiscriminator("type")

sealed trait Stuff
case class Foo(thisIsAString: String, anotherField: Int = 13) extends Stuff
case class Bar(thisIsAString: String, anotherField: Int = 13) extends Stuff

object Foo {
  implicit val decodeBar: Decoder[Bar] = fancy.deriveDecoder
  implicit val encodeBar: ObjectEncoder[Bar] = fancy.deriveEncoder
}

object Bar {
  implicit val decodeBar: Decoder[Bar] = boring.deriveDecoder
  implicit val encodeBar: ObjectEncoder[Bar] = boring.deriveEncoder
}

And then:

scala> val foo: Stuff = Foo("abc", 123)
foo: Stuff = Foo(abc,123)

scala> val bar: Stuff = Bar("xyz", 987)
bar: Stuff = Bar(xyz,987)

scala> val fooJson = foo.asJson
fooJson: io.circe.Json =
{
  "this_is_a_string" : "abc",
  "another_field" : 123,
  "type" : "Foo"
}

scala> val barJson = bar.asJson
barJson: io.circe.Json =
{
  "thisIsAString" : "xyz",
  "anotherField" : 987,
  "type" : "Bar"
}

scala> Decoder[Stuff].decodeJson(fooJson)
res4: io.circe.Decoder.Result[Stuff] = Right(Foo(abc,123))

scala> Decoder[Stuff].decodeJson(barJson)
res5: io.circe.Decoder.Result[Stuff] = Right(Bar(xyz,987))

And you don't have to worry about issues like this.

One footnote about encoding with defaults: unlike argonaut-shapeless and upickle, if you have an instance of a case class with a field value that's the same as that field's default value, it will be included in the JSON representation. This allows us to avoid issues related to equality, and seems to me more natural anyway. This is subject to change in a future version, though.

Enumeration ADTs

Currently the derived codecs for case objects result in either an empty object, or a field with an empty object value, depending on whether the value is statically typed as an ADT leaf or root:

scala> import io.circe.generic.auto._, io.circe.syntax._
import io.circe.generic.auto._
import io.circe.syntax._

scala> sealed trait Base; case object Foo extends Base
defined trait Base
defined object Foo

scala> Foo.asJson.noSpaces
res0: String = {}

scala> (Foo: Base).asJson.noSpaces
res1: String = {"Foo":{}}

This will probably always be the default behavior, since it's always safe and unambiguous, and since having derived encoders that return JSON values that aren't objects complicates things significantly.

Lots of people have asked for this to be configurable, though, because if you're using a sealed trait of case objects to represent something like an enumeration, you often want the values to be represented as strings. This is now possible with io.circe.generic.extras.semiauto:

import io.circe.{ Decoder, Encoder }
import io.circe.generic.extras.semiauto.{ deriveEnumerationDecoder, deriveEnumerationEncoder }
import io.circe.jawn._, io.circe.syntax._

sealed trait Suit extends Product with Serializable
case object Club extends Suit
case object Heart extends Suit
case object Spade extends Suit
case object Diamond extends Suit

object Suit {
  implicit val decodeSuit: Decoder[Suit] = deriveEnumerationDecoder
  implicit val encodeSuit: Encoder[Suit] = deriveEnumerationEncoder
}

And then:

scala> decode[Suit]("\"Club\"")
res0: Either[io.circe.Error,Suit] = Right(Club)

scala> List(Heart, Club, Diamond).asJson
res1: io.circe.Json =
[
  "Heart",
  "Club",
  "Diamond"
]

If we had any case classes in our Suit ADT, the derivation would fail to compile.

Note that this must be explicitly invoked via deriveEnumerationX, and will not collide with any automatically derived decoders.

So why the "extra" part?

The approach I've taken to configuration here is at odds with the [design guidelines] for the project, which prohibit using implicit scope for things like configuration. I'm still working on what I see as a more principled approach, and don't want to commit the core io.circe.generic module to one approach or the other yet.

Right now io.circe.generic.extras is more or less a drop-in replacement for io.circe.generic with some extra functionality. It might become io.circe.generic before the 1.0 release, or it might remain around as something different, but the approach introduced here will continue to be maintained.

New circe-shapeless module

As mentioned above, if you actually need encoders or decoders for Shapeless records, you're not entirely out of luck—you can use the new circe-shapeless module, which also provides instances for ordinary (non-record) hlists and Shapeless's Sized collections.

If you're wondering whether the io.circe.shapeless package name is going to be inconvenient if you import io.circe._ anywhere and also use Shapeless, you're right. This is part of my ongoing passive-aggressive war against libraries that don't use reverse domain names for their packages. The compiler will help you and there's always _root_.

Decoders and the state monad

The Decoder companion object now contains a state object with some methods for constructing StateT[Decoder.Result, ACursor, ?] values. The primary motivation for this addition is that it makes it easier to define decoders that do not accept "unused" fields in a JSON object—see the pull request description for details and examples. Thanks to @olafurpg for the initial motivation and @liff for writing the tests.

Scala.js and null

There are two small changes in circe-scalajs. First, when converting Json values to JavaScript, a null value is no longer converted to undefined, but instead to null (thanks to @jimmydivvy in #376).

The second change is that when converting an arbitrary JavaScript value to a JSON value, unknown values now fail the conversion (instead of silently being converted to null).

circe-testing changes

I've added a few new Arbitrary and Cogen instance to the circe-testing module that was introduced in 0.5.4 (#407). It is also now possible to configure some sizes that aren't tied to the current ScalaCheck size, including the maximum depth of arbitrary JSON objects, and the number of cases to consider when evaluating encoder and decoder equality.

Version updates

The Cats version is now 0.8.0 (#417). Scala.js has been updated from 0.6.11 to 0.6.13, Jawn from 0.9.0 to 0.10.2, refined from 0.5.0 to 0.6.0, Monocle from 1.2.2 to 1.3.1, and iteratee.io from 0.6.1 to 0.7.0. ScalaTest is now 3.0.0, ScalaCheck is 1.13.3, and Discipline is 0.7.1

Other changes

  • It's now possible to derive instances using isomorphisms between types with circe-optics (thanks to @andrelfpinto in #395).
  • Json now has a findAllByKey method (with \\ as an alias) for recursively searching JSON objects with nested objects and arrays by object field key (thanks to @kevinmeredith in #381).
  • The behavior of the jsonDouble prism in circe-optics is changed slightly to make it lawful (#406). These changes involve checking for loss of precision and round-tripping NaN through null.
  • Some type class instance names have been changed for the sake of consistency (#372).
  • The two traversal methods in ACursor that were deprecated in 0.5.0 have been removed (#364).
  • JsonObject now has filter and filterKeys methods for filtering by key and value or just by key (#373).
  • There are now codecs for java.time.LocalTime in circe-java8 (thanks to @darienmt in #427).

Thanks to everyone who contributed code, bug reports, or suggestions!

Downloads

circe 0.5.4

@travisbrown travisbrown released this Oct 17, 2016 · 417 commits to master since this release

This release introduces Scala.js artifacts for circe-optics (thanks, Eric Merritt), and includes two backported bug fixes related to JSON number parsing.

While most users should not be affected by these two bugs, we do encourage everyone to update to 0.5.4, which is fully binary compatible with previous 0.5 releases.

The first issue involves very large BigDecimal values. If you try to parse a number with an exponent larger than Int.MaxValue directly into a BigDecimal, you'll get a NumberFormatException:

scala> val bigDecimal = BigDecimal(s"1e${ Int.MaxValue.toLong + 1L }")
java.lang.NumberFormatException
  at java.math.BigDecimal.<init>(BigDecimal.java:491)
...

It is possible to construct large values like this, though, by moving some of the zeros to the left of the decimal point:

scala> val bigDecimal = BigDecimal(s"100e${ Int.MaxValue }")
bigDecimal: scala.math.BigDecimal = 1.0E+2147483649

If you have a BigDecimal value like this, and you use BiggerDecimal.fromBigDecimal to convert it into a BiggerDecimal, you'll get an incorrect value in 0.5.3 or earlier:

scala> import io.circe.numbers.{ BiggerDecimal, NumberParsing }
import io.circe.numbers.BiggerDecimal

scala> val fromBigDecimal = BiggerDecimal.fromBigDecimal(bigDecimal.bigDecimal)
fromBigDecimal: io.circe.numbers.BiggerDecimal = 1e2147483645

scala> val Some(fromString) = NumberParsing.parseBiggerDecimal(bigDecimal.toString)
fromString: io.circe.numbers.BiggerDecimal = 1e2147483649

scala> fromBigDecimal == fromString
res0: Boolean = false

The second bug involves situations where multiples of ten with zeros after a decimal point were not being correctly converted into canonical form. While this does not affect decoding, it does affect equality comparisons for JsonNumber:

scala> import io.circe.{ Json, JsonNumber }
import io.circe.{Json, JsonNumber}

scala> val fromDouble = Json.fromDouble(10.0)
fromDouble: Option[io.circe.Json] = Some(10.0)

scala> val fromString = Json.fromJsonNumber(JsonNumber.unsafeDecimal("10.0"))
fromString: io.circe.Json = 10.0

scala> fromDouble.get == fromString
res0: Boolean = false

Decoding these values into Double, BigDecimal, Long, or any other representation would result in correct comparisons.

Downloads

circe 0.5.3

@travisbrown travisbrown released this Oct 7, 2016 · 417 commits to master since this release

This release includes backported fixes for three bugs and one entirely new module.

Binary compatibility is now confirmed by MiMa.

Bug fixes

In some cases it was possible to inadvertently construct decoders with inconsistent fail-fast and error-accumulating behavior when mapping over an Option decoder (#388). This has been fixed by overriding tryDecodeAccumulating appropriately in map and flatMap decoders (#389).

The second bug simply made Decoder#validate completely unusable because of an infinitely recursive call that's been removed (#397).

The third bug was that BiggerDecimal wasn't Serializable. Now it is (#399).

Thanks to Thomas Dufour, Julien Jean Paul Sirocchi, and Jeremy Smith for the report and fixes.

New testing module

This release also introduces a new circe-testing module that you can use to test your own circe codecs. Suppose for example that you have a case class like this:

import cats.Eq
import io.circe.{ Decoder, Encoder }

case class Foo(i: Int, s: String)

object Foo {
  implicit val decodeFoo: Decoder[Foo] =
    Decoder.forProduct2("someNumber", "someString")(Foo.apply)

  implicit val encodeFoo: Encoder[Foo] =
    Encoder.forProduct2("someNumber", "someString")(foo => (foo.i, foo.s))

  implicit val eqFoo: Eq[Foo] = Eq.fromUniversalEquals
}

Now you can add a circe-testing test dependency to your build and write the following:

import io.circe.testing.CodecTests
import org.scalacheck.Arbitrary

implicit val arbitraryFoo: Arbitrary[Foo] = Arbitrary(
  for {
    i <- Arbitrary.arbitrary[Int]
    s <- Arbitrary.arbitrary[String]
  } yield Foo(i, s)
)

And then:

scala> CodecTests[Foo].codec.all.check
+ codec.consistency with accumulating: OK, passed 100 tests.
+ codec.roundTrip: OK, passed 100 tests.

(You can of course check the laws using ScalaTest, Specs 2, or whatever other method you want.)

Downloads

circe 0.5.2

@travisbrown travisbrown released this Sep 17, 2016 · 417 commits to master since this release

This release includes a single change to circe-generic (#366) that does not affect behavior but does speed up compilation for generic derivation in many cases.

Downloads

circe 0.5.1

@travisbrown travisbrown released this Sep 4, 2016 · 417 commits to master since this release

This release contains no changes to 0.5.0 apart from updating the Cats dependency to 0.7.2 so that users who depend on Cats transitively are not exposed to this bug in Cats 0.7.0.

Downloads