Skip to content

Commit

Permalink
Merge pull request #89 from GeluOltean/master
Browse files Browse the repository at this point in the history
Define ShortClassNameSnakeCase TypeTags
  • Loading branch information
julienrf authored Jul 8, 2022
2 parents 3a7908a + e0496fc commit a744873
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 4 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ ThisBuild / scalaVersion := "2.13.3"

ThisBuild / crossScalaVersions := Seq(scalaVersion.value, "2.12.8")

ThisBuild / versionPolicyIntention := Compatibility.BinaryAndSourceCompatible
ThisBuild / versionPolicyIntention := Compatibility.BinaryCompatible
// Temporary, because version 10.0.0 was invalid
ThisBuild / versionPolicyPreviousVersions := Seq("10.0.1")

Expand Down
16 changes: 15 additions & 1 deletion library/src/main/scala/julienrf/json/derived/typetags.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package julienrf.json.derived

import play.api.libs.json.{JsObject, JsResult, JsValue, Json, OFormat, OWrites, Reads, Writes, __}
import play.api.libs.json.{JsObject, JsValue, Json, OFormat, OWrites, Reads, Writes, __}
import shapeless.Witness
import shapeless.labelled.FieldType
import scala.language.higherKinds
Expand Down Expand Up @@ -223,6 +223,16 @@ object TypeTag {
}
}

/** Use the class name converted to use snake_case as a type tag */
trait ShortClassNameSnakeCase[A] extends TypeTag[A]

object ShortClassNameSnakeCase {
implicit def fromWitness[K <: Symbol, A](implicit wt: Witness.Aux[K]): ShortClassNameSnakeCase[FieldType[K, A]] =
new ShortClassNameSnakeCase[FieldType[K, A]] {
def value: String = play.api.libs.json.JsonNaming.SnakeCase.apply(wt.value.name)
}
}

/** Use the fully qualified JVM name of the class as a type tag */
trait FullClassName[A] extends TypeTag[A]

Expand Down Expand Up @@ -258,6 +268,10 @@ object TypeTagSetting {
type Value[A] = TypeTag.ShortClassName[A]
}

object ShortClassNameSnakeCase extends TypeTagSetting {
type Value[A] = TypeTag.ShortClassNameSnakeCase[A]
}

object FullClassName extends TypeTagSetting {
type Value[A] = TypeTag.FullClassName[A]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.scalacheck.{Arbitrary, Gen}
import org.scalacheck.Arbitrary.arbitrary
import org.scalatest.featurespec.AnyFeatureSpec
import org.scalatestplus.scalacheck.Checkers
import play.api.libs.json.{Format, JsNumber, Json, OFormat, OWrites, Reads, Writes, __}
import play.api.libs.json.{Format, JsArray, JsNumber, JsObject, Json, OFormat, OWrites, Reads, Writes, __}

class DerivedOFormatSuite extends AnyFeatureSpec with Checkers {

Expand Down Expand Up @@ -161,7 +161,10 @@ class DerivedOFormatSuite extends AnyFeatureSpec with Checkers {
}
}

Feature("user-defined type tags") {
Feature("type tags") {
import TestHelpers._
import julienrf.json.derived

Scenario("user-defined type tags") {
sealed trait Foo
case class Bar(x: Int) extends Foo
Expand All @@ -177,6 +180,23 @@ class DerivedOFormatSuite extends AnyFeatureSpec with Checkers {
assert(json == Json.obj("_bar_" -> Json.obj("x" -> 42)))
assert(fooFormat.reads(json).asEither == Right(foo))
}

Scenario("ShortClassNameSnakeCase should format tag names using snake casing") {
implicit lazy val demoClassFormat: OFormat[CompositeNameClass] = derived
.withTypeTag
.oformat[CompositeNameClass](TypeTagSetting.ShortClassNameSnakeCase)

val inner = Seq(FooBar(true), fooBarry(true), foo_barrier(true))
val barFoo: CompositeNameClass = BarFoo(inner)
val parsed = Json.toJsObject(barFoo)
val parsedInner = ((parsed \ "bar_foo") \ "inner")
.as[JsArray]
.value
.map(_.as[JsObject])

assert(validSnakeNames.contains(parsed.keys.head))
assert(parsedInner.map(_.keys.head).forall(validSnakeNames.contains))
}
}

Feature("user-defined implicits") {
Expand Down Expand Up @@ -326,4 +346,14 @@ object TestHelpers {
case class X(a: Int) extends ADTBase
case class Y(b: String) extends ADTBase
case class Z(l: ADTBase, r: ADTBase) extends ADTBase

sealed trait CompositeNameClass

case class FooBar(inner: Boolean) extends CompositeNameClass
case class fooBarry(inner: Boolean) extends CompositeNameClass
case class foo_barrier(inner: Boolean) extends CompositeNameClass
case class BarFoo(inner: Seq[CompositeNameClass]) extends CompositeNameClass

lazy val validSnakeNames: Set[String] =
Set("foo_bar", "foo_barry", "foo_barrier", "bar_foo")
}

0 comments on commit a744873

Please sign in to comment.