Skip to content

Commit

Permalink
WIP update
Browse files Browse the repository at this point in the history
  • Loading branch information
kiroco12 committed Dec 3, 2019
1 parent a809106 commit 147e54b
Show file tree
Hide file tree
Showing 24 changed files with 544 additions and 165 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
@@ -1,7 +1,7 @@
language: scala

scala:
- 2.11.11
- 2.12.10

jdk:
- oraclejdk8
Expand Down
30 changes: 17 additions & 13 deletions build.sbt
@@ -1,25 +1,29 @@
val scalaExercisesV = "0.4.0-SNAPSHOT"
val circeVersion = "0.7.0"
import ProjectPlugin.autoImport._

val scalaExercisesV = "0.5.0-SNAPSHOT"

def dep(artifactId: String) = "org.scala-exercises" %% artifactId % scalaExercisesV

lazy val `circe` = (project in file("."))
.enablePlugins(ExerciseCompilerPlugin)
.settings(
name := "exercises-circe",
name := "exercises-circe",
libraryDependencies ++= Seq(
dep("exercise-compiler"),
dep("definitions"),
%%("circe-core"),
%%("circe-generic"),
%%("circe-parser"),
"io.circe" %% "circe-optics" % circeVersion,
%%("shapeless"),
%%("cats-core"),
%%("scalatest"),
%%("scalacheck"),
%%("scheckShapeless")
)
"io.circe" %% "circe-core" % V.circe,
"io.circe" %% "circe-generic" % V.circe,
"io.circe" %% "circe-parser" % V.circe,
"io.circe" %% "circe-generic-extras" % V.circeGenericExtras,
"io.circe" %% "circe-shapes" % V.circe,
"io.circe" %% "circe-optics" % V.circeOptics,
%%("shapeless", V.shapeless),
%%("cats-core", V.cats),
%%("scalatest", V.scalatest),
%%("scalacheck", V.scalacheck),
"com.github.alexarchambault" %% "scalacheck-shapeless_1.14" % V.scalacheckShapeless
),
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
)

// Distribution
Expand Down
42 changes: 27 additions & 15 deletions project/ProjectPlugin.scala
@@ -1,17 +1,35 @@
import de.heikoseeberger.sbtheader.HeaderPattern
import scala.language.reflectiveCalls
import de.heikoseeberger.sbtheader.HeaderPlugin.autoImport._
import de.heikoseeberger.sbtheader.License._
import sbt.Keys._
import sbt._
import sbtorgpolicies.OrgPoliciesPlugin.autoImport._
import sbtorgpolicies._
import sbtorgpolicies.model._
import sbtorgpolicies.OrgPoliciesPlugin.autoImport._

object ProjectPlugin extends AutoPlugin {

override def trigger: PluginTrigger = allRequirements

override def requires: Plugins = plugins.JvmPlugin && OrgPoliciesPlugin

object autoImport {

lazy val V = new {
val scala212: String = "2.12.10"
val cats: String = "2.0.0"
val circe: String = "0.12.3"
val circeOptics: String = "0.12.0"
val circeGenericExtras: String = "0.12.2"
val shapeless: String = "2.3.3"
val scalatest: String = "3.0.8"
val scalacheck: String = "1.14.2"
val scalacheckShapeless: String = "1.2.3"
}
}

import autoImport._

override def projectSettings: Seq[Def.Setting[_]] =
Seq(
description := "Scala Exercises: The path to enlightenment",
Expand All @@ -25,23 +43,17 @@ object ProjectPlugin extends AutoPlugin {
organizationEmail = "hello@47deg.com"
),
orgLicenseSetting := ApacheLicense,
scalaVersion := "2.11.11",
scalaVersion := V.scala212,
scalaOrganization := "org.scala-lang",
crossScalaVersions := Seq("2.11.11"),
resolvers ++= Seq(
Resolver.mavenLocal,
Resolver.sonatypeRepo("snapshots"),
Resolver.sonatypeRepo("releases")
),
scalacOptions := sbtorgpolicies.model.scalacCommonOptions,
headers := Map(
"scala" -> (HeaderPattern.cStyleBlockComment,
s"""|/*
| * scala-exercises - ${name.value}
| * Copyright (C) 2015-2016 47 Degrees, LLC. <http://www.47deg.com>
| */
|
|""".stripMargin)
)
scalacOptions ++= scalacCommonOptions,
headerLicense := Some(Custom(s"""| scala-exercises - ${name.value}
| Copyright (C) 2015-2019 47 Degrees, LLC. <http://www.47deg.com>
|
|""".stripMargin))
)
}
}
2 changes: 1 addition & 1 deletion project/build.properties
@@ -1 +1 @@
sbt.version=0.13.13
sbt.version=1.2.8
4 changes: 2 additions & 2 deletions project/plugins.sbt
Expand Up @@ -2,5 +2,5 @@ resolvers ++= Seq(
Resolver.sonatypeRepo("snapshots")
)

addSbtPlugin("org.scala-exercises" % "sbt-exercise" % "0.4.0-SNAPSHOT", "0.13", "2.10")
addSbtPlugin("com.47deg" % "sbt-org-policies" % "0.5.13")
addSbtPlugin("org.scala-exercises" % "sbt-exercise" % "0.5.0-SNAPSHOT")
addSbtPlugin("com.47deg" % "sbt-org-policies" % "0.12.0-M3")
155 changes: 155 additions & 0 deletions src/main/scala/circelib/ADTSection.scala
@@ -0,0 +1,155 @@
/*
* scala-exercises - exercises-circe
* Copyright (C) 2015-2019 47 Degrees, LLC. <http://www.47deg.com>
*
*/

package circelib

import io.circe.Error
import io.circe.syntax._
import org.scalaexercises.definitions.Section
import org.scalatest.{FlatSpec, Matchers}

/**
* ==ADTs encoding and decoding==
*
* The most straightforward way to encode / decode ADTs is by using generic derivation for the case classes but
* explicitly defined instances for the ADT type.
*
* @param name ADT (Algebraic Data Types)
*/
object ADTSection extends FlatSpec with Matchers with Section {

import helpers.ADTHelpers._

/**
* Consider the following ADT:
* {{{
* sealed trait Event
*
* case class Foo(i: Int) extends Event
* case class Bar(s: String) extends Event
* case class Baz(c: Char) extends Event
* case class Qux(values: List[String]) extends Event
* }}}
*
* And the encoder / decoder instances:
* {{{
* import cats.syntax.functor._
* import io.circe.{ Decoder, Encoder }, io.circe.generic.auto._
* import io.circe.syntax._
*
* object GenericDerivation {
* implicit val encodeEvent: Encoder[Event] = Encoder.instance {
* case foo @ Foo(_) => foo.asJson
* case bar @ Bar(_) => bar.asJson
* case baz @ Baz(_) => baz.asJson
* case qux @ Qux(_) => qux.asJson
* }
*
* implicit val decodeEvent: Decoder[Event] =
* List[Decoder[Event]](
* Decoder[Foo].widen,
* Decoder[Bar].widen,
* Decoder[Baz].widen,
* Decoder[Qux].widen
* ).reduceLeft(_ or _)
* }
* }}}
*
* Note that we have to call widen (which is provided by Cats’s Functor syntax, which we bring into scope with
* the first import) on the decoders because the Decoder type class is not covariant. The invariance of circe’s
* type classes is a matter of some controversy (Argonaut for example has gone from invariant to covariant and
* back), but it has enough benefits that it’s unlikely to change, which means we need workarounds like this
* occasionally.
*
* It’s also worth noting that our explicit Encoder and Decoder instances will take precedence over the
* generically-derived instances we would otherwise get from the io.circe.generic.auto._ import (see slides from
* Travis Brown’s talk here for some discussion of how this prioritization works).
*
* We can use these instances like this:
*/
def genericDerivation(res0: Either[Error, Foo], res1: String) = {
import GenericDerivation._
import io.circe.parser.decode

decode[Event]("""{ "i": 1000 }""") shouldBe res0
(Foo(100): Event).asJson.noSpaces shouldBe res1
}

// TODO: Example from documentation not compiling (https://github.com/circe/circe/issues/1125)
/*
/**
* =A more generic solution=
*
* We can avoid the fuss of writing out all the cases by using the `circe-shapes` module:
* {{{
* // To suppress previously imported implicit codecs.
* import GenericDerivation.{ decodeEvent => _, encodeEvent => _ }
*
* object ShapesDerivation {
* import io.circe.shapes
* import shapeless.{ Coproduct, Generic }
*
* implicit def encodeAdtNoDiscr[A, Repr <: Coproduct](implicit
* gen: Generic.Aux[A, Repr],
* encodeRepr: Encoder[Repr]
* ): Encoder[A] = encodeRepr.contramap(gen.to)
*
* implicit def decodeAdtNoDiscr[A, Repr <: Coproduct](implicit
* gen: Generic.Aux[A, Repr],
* decodeRepr: Decoder[Repr]
* ): Decoder[A] = decodeRepr.map(gen.from)
*
* }
*
* And then:
* }}}
*/
def shapesDerivation(res0: Either[Error, Event], res1: String) = {
import ShapesDerivation._
import io.circe.parser.decode, io.circe.syntax._
decode[Event]("""{ "i": 1000 }""") shouldBe res0
(Foo(100): Event).asJson.noSpaces shouldBe res1
}
/**
* This will work for any ADT anywhere that `encodeAdtNoDiscr` and `decodeAdtNoDiscr` are in scope. If we wanted it
* to be more limited, we could replace the generic `A` with our ADT types in those definitions, or we could make
* the definitions non-implicit and define implicit instances explicitly for the ADTs we want encoded this way.
*
* The main drawback of this approach (apart from the extra `circe-shapes` dependency) is that the constructors
* will be tried in alphabetical order, which may not be what we want if we have ambiguous case classes (where
* the member names and types are the same).
*/
*/

/**
* =The future=
*
* The generic-extras module provides a little more configurability in this respect. We can write the following,
* for example:
* {{{
* // Same as above
* import ShapesDerivation.{encodeAdtNoDiscr => _, decodeAdtNoDiscr => _}
*
* object GenericExtraDerivation {
* import io.circe.generic.extras.Configuration
*
* implicit val genDevConfig: Configuration =
* Configuration.default.withDiscriminator("what_am_i")
* }
* }}}
*/
def genericExtrasADT(res0: Either[Error, Event]) = {
import GenericExtraDerivation._
import io.circe.parser.decode
import io.circe.generic.extras.auto._

decode[Event]("""{ "i": 1000, "what_am_i": "Foo" }""") shouldBe res0
}

}
5 changes: 3 additions & 2 deletions src/main/scala/circelib/CirceLibrary.scala
@@ -1,6 +1,7 @@
/*
* scala-exercises - exercises-circe
* Copyright (C) 2015-2016 47 Degrees, LLC. <http://www.47deg.com>
* scala-exercises - exercises-circe
* Copyright (C) 2015-2019 47 Degrees, LLC. <http://www.47deg.com>
*
*/

package circelib
Expand Down

0 comments on commit 147e54b

Please sign in to comment.