diff --git a/build.sbt b/build.sbt index 00356db6..a59d5f34 100644 --- a/build.sbt +++ b/build.sbt @@ -6,15 +6,15 @@ val repo = "git@github.com:jto/validation.git" val org = "io.github.jto" val license = ("Apache License", url("http://www.apache.org/licenses/LICENSE-2.0.txt")) -val catsVersion = "1.2.0" -val jodaConvertVersion = "2.1.1" -val jodaTimeVersion = "2.10" -val kindProjectorVersion = "0.9.7" -val parserCombinatorsVersion = "1.1.1" -val playVersion = "2.6.3" -val scalacVersion = "2.12.6" -val scalatestVersion = "3.2.0-SNAP10" -val scalaXmlVersion = "1.1.0" +val catsVersion = "2.0.0" +val jodaConvertVersion = "2.2.1" +val jodaTimeVersion = "2.10.4" +val kindProjectorVersion = "0.10.3" +val parserCombinatorsVersion = "1.1.2" +val playVersion = "2.7.4" +val scalacVersion = "2.13.0" +val scalatestVersion = "3.0.8" +val scalaXmlVersion = "1.2.0" lazy val root = aggregate("validation", validationJVM, validationJS, docs).in(file(".")) lazy val validationJVM = aggregate("validationJVM", coreJVM, formJVM, delimitedJVM, jsonAstJVM, `validation-playjson`, `validation-xml`, `date-tests`) @@ -25,6 +25,7 @@ lazy val `validation-core` = .crossType(CrossType.Pure) .settings(validationSettings: _*) .settings(generateBoilerplate: _*) + .settings(libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value) lazy val coreJVM = `validation-core`.jvm lazy val coreJS = `validation-core`.js lazy val core = aggregate("validation-core", coreJVM, coreJS) @@ -111,16 +112,17 @@ lazy val validationSettings = settings ++ dependencies ++ doPublish ++ scoverage lazy val settings = Seq( scalaVersion := scalacVersion, - crossScalaVersions := Seq(scalacVersion, "2.11.12"), + crossScalaVersions := Seq(scalacVersion, "2.12.10", "2.11.12"), organization := org, scalacOptions ++= commonScalacOptions, scalacOptions in (Compile, console) ~= (_ filterNot (_ == "-Ywarn-unused-import")), scalacOptions in (Test, console) := (scalacOptions in (Compile, console)).value.filterNot(_ == "-Xfatal-warnings"), + scalacOptions -= "-Xfatal-warnings", resolvers ++= commonResolvers, testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-oDF"), scalaJSStage in Global := FastOptStage, parallelExecution := false, - libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.14.0" % "test" + libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.14.1" % "test" ) val commonScalacOptions = Seq( @@ -136,13 +138,11 @@ val commonScalacOptions = Seq( "-Xfatal-warnings", "-Xlint", // "-Yinline-warnings", - "-Yno-adapted-args", "-Ywarn-dead-code", "-Ywarn-numeric-widen", "-Ywarn-value-discard", - "-Ywarn-unused-import", "-Xfuture", - "-Ypartial-unification" + //"-Ypartial-unification" ) val commonResolvers = Seq( @@ -157,7 +157,7 @@ val dependencies = Seq( "joda-time" % "joda-time" % jodaTimeVersion, "org.joda" % "joda-convert" % jodaConvertVersion ), - addCompilerPlugin("org.spire-math" %% "kind-projector" % kindProjectorVersion) + addCompilerPlugin("org.typelevel" %% "kind-projector" % kindProjectorVersion) ) val generateBoilerplate = Seq( diff --git a/project/build.properties b/project/build.properties index 5620cc50..8522443d 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.1 +sbt.version=1.3.2 diff --git a/project/plugins.sbt b/project/plugins.sbt index e2ca7f44..6def02a7 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,15 +1,28 @@ resolvers += Resolver.url( "tpolecat-sbt-plugin-releases", - url("http://dl.bintray.com/content/tpolecat/sbt-plugin-releases"))( + url("https://dl.bintray.com/content/tpolecat/sbt-plugin-releases"))( Resolver.ivyStylePatterns) -addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.6.7") +addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.6.12") + +addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.6.1") + +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.29") + +addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.1") + +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.0") + +addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.7") + +addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.4.2") + +addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.4.3") + +addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.1.8") + +addSbtPlugin("org.duhemm" % "sbt-errors-summary" % "0.6.3") -addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.6.0") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.24") -addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1") -addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.5") \ No newline at end of file diff --git a/validation-core/src/main/scala/Formatter.scala b/validation-core/src/main/scala/Formatter.scala index 8e945c66..1cff4a90 100644 --- a/validation-core/src/main/scala/Formatter.scala +++ b/validation-core/src/main/scala/Formatter.scala @@ -65,7 +65,7 @@ object To { w.writes(o) } -case class Reader[I](path: Path = Path(Nil)) { +final case class Reader[I](path: Path = Path(Nil)) { /** * When applied, the rule will lookup for data at the given path, and apply the `sub` Rule on it @@ -112,7 +112,7 @@ case class Reader[I](path: Path = Path(Nil)) { def \(child: PathNode): Reader[I] = Reader(path \ child) } -case class Writer[I](path: Path = Path(Nil)) { +final case class Writer[I](path: Path = Path(Nil)) { /** * Create a Write that convert data to type `I`, and put it at Path `path` @@ -156,7 +156,7 @@ object Formatting { def apply[IR, IW] = new Formatting[IR, IW] {} } -case class Formatter[IR, IW](path: Path = Path(Nil)) { +final case class Formatter[IR, IW](path: Path = Path(Nil)) { def format[JJ, J, O](subR: => RuleLike[J, O], subW: => WriteLike[O, JJ])( implicit r: Path => RuleLike[IR, J], diff --git a/validation-core/src/main/scala/Path.scala b/validation-core/src/main/scala/Path.scala index 67bea24d..ba820a80 100644 --- a/validation-core/src/main/scala/Path.scala +++ b/validation-core/src/main/scala/Path.scala @@ -1,11 +1,11 @@ package jto.validation sealed trait PathNode -case class KeyPathNode(key: String) extends PathNode { +final case class KeyPathNode(key: String) extends PathNode { override def toString = key } -case class IdxPathNode(idx: Int) extends PathNode { +final case class IdxPathNode(idx: Int) extends PathNode { override def toString = s"[$idx]" } diff --git a/validation-core/src/main/scala/SyntaxObs.scala b/validation-core/src/main/scala/SyntaxObs.scala index fb40327e..31c51be1 100644 --- a/validation-core/src/main/scala/SyntaxObs.scala +++ b/validation-core/src/main/scala/SyntaxObs.scala @@ -1,6 +1,6 @@ package jto.validation -case class ~[A, B](_1: A, _2: B) +final case class ~[A, B](_1: A, _2: B) trait SyntaxCombine[M[_]] { def apply[A, B](ma: M[A], mb: M[B]): M[A ~ B] diff --git a/validation-core/src/main/scala/ValidationError.scala b/validation-core/src/main/scala/ValidationError.scala index df4caf0b..92961637 100644 --- a/validation-core/src/main/scala/ValidationError.scala +++ b/validation-core/src/main/scala/ValidationError.scala @@ -6,7 +6,7 @@ package jto.validation * @param message the error message * @param args the error message arguments */ -case class ValidationError(messages: Seq[String], args: Any*) { +final case class ValidationError(messages: Seq[String], args: Any*) { lazy val message = messages.last } diff --git a/validation-delimited/src/test/scala/RulesSpec.scala b/validation-delimited/src/test/scala/RulesSpec.scala index 65fee8c5..bf0fc30f 100644 --- a/validation-delimited/src/test/scala/RulesSpec.scala +++ b/validation-delimited/src/test/scala/RulesSpec.scala @@ -6,7 +6,7 @@ import org.scalatest._ class RulesSpec extends WordSpec with Matchers { "Rules" should { "demonstrate typical usage" in { - case class Contact(name: String, email: String, birthday: Option[String]) + final case class Contact(name: String, email: String, birthday: Option[String]) val contactReads = From[Delimited] { __ => ( diff --git a/validation-form/src/main/scala/Writes.scala b/validation-form/src/main/scala/Writes.scala index 1014293d..3d116843 100644 --- a/validation-form/src/main/scala/Writes.scala +++ b/validation-form/src/main/scala/Writes.scala @@ -34,7 +34,7 @@ trait Writes implicit def mapW[I](implicit w: WriteLike[I, Seq[String]]) = Write[Map[String, I], PM] { m => - toPM(m.mapValues(w.writes)) + toPM(m.map { case (k,v) => k -> w.writes(v) } ) } implicit def spm[O](implicit w: WriteLike[O, PM]) = diff --git a/validation-form/src/test/scala/FormatSpec.scala b/validation-form/src/test/scala/FormatSpec.scala index e0cd47b1..e2a6a828 100644 --- a/validation-form/src/test/scala/FormatSpec.scala +++ b/validation-form/src/test/scala/FormatSpec.scala @@ -4,7 +4,7 @@ import org.scalatest._ import scala.Function.unlift class FormatSpec extends WordSpec with Matchers { - case class User(id: Long, name: String) + final case class User(id: Long, name: String) val luigi = User(1, "Luigi") "Format" should { @@ -389,7 +389,7 @@ class FormatSpec extends WordSpec with Matchers { } "format recursive" when { - case class RecUser(name: String, friends: Seq[RecUser] = Nil) + final case class RecUser(name: String, friends: Seq[RecUser] = Nil) val u = RecUser("bob", Seq(RecUser("tom"))) val m = Map("name" -> Seq("bob"), diff --git a/validation-form/src/test/scala/MacroSpec.scala b/validation-form/src/test/scala/MacroSpec.scala index 92080650..33d84c3d 100644 --- a/validation-form/src/test/scala/MacroSpec.scala +++ b/validation-form/src/test/scala/MacroSpec.scala @@ -2,28 +2,28 @@ import jto.validation._ import jto.validation.forms._ import org.scalatest._ -case class User(age: Int, name: String) -case class Dog(name: String, master: User) -case class Cat(name: String) -case class RecUser(name: String, +final case class User(age: Int, name: String) +final case class Dog(name: String, master: User) +final case class Cat(name: String) +final case class RecUser(name: String, cat: Option[Cat] = None, hobbies: Seq[String] = Seq(), friends: Seq[RecUser] = Seq()) -case class User1(name: String, friend: Option[User1] = None) -case class UserMap(name: String, friends: Map[String, UserMap] = Map()) +final case class User1(name: String, friend: Option[User1] = None) +final case class UserMap(name: String, friends: Map[String, UserMap] = Map()) -case class Toto(name: String) -case class Toto2(name: Option[String]) -case class Toto3(name: List[Double]) -case class Toto4(name: Set[Long]) -case class Toto5(name: Map[String, Int]) -case class Toto6(name: Seq[Dog]) -case class UserFail(name: String, bd: Toto) +final case class Toto(name: String) +final case class Toto2(name: Option[String]) +final case class Toto3(name: List[Double]) +final case class Toto4(name: Set[Long]) +final case class Toto5(name: Map[String, Int]) +final case class Toto6(name: Seq[Dog]) +final case class UserFail(name: String, bd: Toto) -case class Id[A](id: A) -case class C1[A](id: Id[A], name: String) +final case class Id[A](id: A) +final case class C1[A](id: Id[A], name: String) -case class X( +final case class X( _1: String, _2: String, _3: String, @@ -47,7 +47,7 @@ case class X( _21: String ) -case class Program(id: Long, +final case class Program(id: Long, name: String, logoPath: Option[String], logoThumb: Option[String]) @@ -55,7 +55,7 @@ object Program { def programs = List.empty[Program] } -case class Person(name: String, age: Int) +final case class Person(name: String, age: Int) object Person { implicit val personRule = { import Rules._ @@ -67,7 +67,7 @@ object Person { } } -case class Person2(names: List[String]) +final case class Person2(names: List[String]) object Person2 { implicit val personRule = { diff --git a/validation-form/src/test/scala/RulesSpec.scala b/validation-form/src/test/scala/RulesSpec.scala index c72d80c0..b0f768c7 100644 --- a/validation-form/src/test/scala/RulesSpec.scala +++ b/validation-form/src/test/scala/RulesSpec.scala @@ -450,9 +450,9 @@ class RulesSpec extends WordSpec with Matchers { "validate subclasses (and parse the concrete class)" when { - trait A - case class B(foo: Int) extends A - case class C(bar: Int) extends A + sealed trait A + final case class B(foo: Int) extends A + final case class C(bar: Int) extends A val b = Map("name" -> Seq("B"), "foo" -> Seq("4")) val c = Map("name" -> Seq("C"), "bar" -> Seq("6")) @@ -498,12 +498,12 @@ class RulesSpec extends WordSpec with Matchers { } "perform complex validation" in { - case class Contact(firstname: String, + final case class Contact(firstname: String, lastname: String, company: Option[String], informations: Seq[ContactInformation]) - case class ContactInformation( + final case class ContactInformation( label: String, email: Option[String], phones: Seq[String]) val validM = Map("firstname" -> Seq("Julien"), @@ -596,14 +596,14 @@ class RulesSpec extends WordSpec with Matchers { } "read recursive" when { - case class RecUser(name: String, friends: Seq[RecUser] = Nil) + final case class RecUser(name: String, friends: Seq[RecUser] = Nil) val u = RecUser("bob", Seq(RecUser("tom"))) val m = Map("name" -> Seq("bob"), "friends[0].name" -> Seq("tom"), "friends[0].friends" -> Seq()) - case class User1(name: String, friend: Option[User1] = None) + final case class User1(name: String, friend: Option[User1] = None) val u1 = User1("bob", Some(User1("tom"))) val m1 = Map("name" -> Seq("bob"), "friend.name" -> Seq("tom")) diff --git a/validation-form/src/test/scala/WritesSpec.scala b/validation-form/src/test/scala/WritesSpec.scala index 7311a348..a5aa573e 100644 --- a/validation-form/src/test/scala/WritesSpec.scala +++ b/validation-form/src/test/scala/WritesSpec.scala @@ -5,12 +5,12 @@ import scala.Function.unlift class WritesSpec extends WordSpec with Matchers { - case class Contact(firstname: String, + final case class Contact(firstname: String, lastname: String, company: Option[String], informations: Seq[ContactInformation]) - case class ContactInformation( + final case class ContactInformation( label: String, email: Option[String], phones: Seq[String]) val contact = Contact( @@ -53,7 +53,7 @@ class WritesSpec extends WordSpec with Matchers { "n" -> Seq("5")) (Path \ "n").write(optionW(intW)).writes(None) shouldBe Map.empty - case class Foo(name: String) + final case class Foo(name: String) implicit val wf = (Path \ "name").write[String, UrlFormEncoded].contramap((_: Foo).name) val wmf = (Path \ "maybe").write[Option[Foo], UrlFormEncoded] @@ -346,12 +346,12 @@ class WritesSpec extends WordSpec with Matchers { } "write recursive" when { - case class RecUser(name: String, friends: List[RecUser] = Nil) + final case class RecUser(name: String, friends: List[RecUser] = Nil) val u = RecUser("bob", List(RecUser("tom"))) val m = Map("name" -> Seq("bob"), "friends[0].name" -> Seq("tom")) - case class User1(name: String, friend: Option[User1] = None) + final case class User1(name: String, friend: Option[User1] = None) val u1 = User1("bob", Some(User1("tom"))) val m1 = Map("name" -> Seq("bob"), "friend.name" -> Seq("tom")) @@ -404,7 +404,7 @@ class WritesSpec extends WordSpec with Matchers { } object TestValueClass { - case class Id(value: String) extends AnyVal + final case class Id(value: String) extends AnyVal object Id { implicit val writes: Write[Id, String] = Write(id => id.value) } diff --git a/validation-jsjson/src/main/scala/Rules.scala b/validation-jsjson/src/main/scala/Rules.scala index 7ade375e..470ded7a 100644 --- a/validation-jsjson/src/main/scala/Rules.scala +++ b/validation-jsjson/src/main/scala/Rules.scala @@ -12,7 +12,7 @@ trait Rules extends DefaultRules[js.Dynamic] { case j => Invalid(Seq(ValidationError(msg, args: _*))) }) - implicit def stringR = + implicit def stringR: Rule[js.Dynamic, String] = jsonAs[String] { case v if (v: Any).isInstanceOf[String] => Valid(v.asInstanceOf[String]) }("error.invalid", "String") @@ -46,29 +46,29 @@ trait Rules extends DefaultRules[js.Dynamic] { Valid(v.asInstanceOf[js.Dictionary[js.Dynamic]]) }("error.invalid", "Object") - implicit def jsArrayR[A] = + implicit def jsArrayR[A]: Rule[js.Dynamic, js.Array[A]] = jsonAs[js.Array[A]] { case v: js.Array[_] => Valid(v.asInstanceOf[js.Array[A]]) }("error.invalid", "Array") - implicit def floatR = + implicit def floatR: Rule[js.Dynamic, Float] = jsonAs[Float] { case v if v.isInstanceOf[Float] => Valid(v.asInstanceOf[Float]) }("error.number", "Float") - implicit def doubleR = + implicit def doubleR: Rule[js.Dynamic, Double] = jsonAs[Double] { case v if v.isInstanceOf[Double] => Valid(v.asInstanceOf[Double]) }("error.number", "Double") - implicit def bigDecimal = + implicit def bigDecimal: Rule[js.Dynamic, BigDecimal] = jsonAs[BigDecimal] { case v if Try(BigDecimal(v.toString)).isSuccess => Valid(BigDecimal(v.toString)) }("error.number", "BigDecimal") import java.{math => jm} - implicit def javaBigDecimal = + implicit def javaBigDecimal: Rule[js.Dynamic, java.math.BigDecimal] = jsonAs[jm.BigDecimal] { case v if Try(new jm.BigDecimal(v.toString)).isSuccess => Valid(new jm.BigDecimal(v.toString)) @@ -129,7 +129,7 @@ trait Rules extends DefaultRules[js.Dynamic] { // XXX: a bit of boilerplate private def pickInS[T]( implicit r: RuleLike[Seq[js.Dynamic], T]): Rule[js.Dynamic, T] = - jsArrayR[js.Dynamic].map(fs => Seq(fs: _*)).andThen(r) + jsArrayR[js.Dynamic].map(_.toSeq).andThen(r) implicit def pickSeq[O](implicit r: RuleLike[js.Dynamic, O]) = pickInS(seqR[js.Dynamic, O]) implicit def pickSet[O](implicit r: RuleLike[js.Dynamic, O]) = diff --git a/validation-jsjson/src/test/scala/FormatSpec.scala b/validation-jsjson/src/test/scala/FormatSpec.scala index 32a38b1b..8f99eb01 100644 --- a/validation-jsjson/src/test/scala/FormatSpec.scala +++ b/validation-jsjson/src/test/scala/FormatSpec.scala @@ -1,3 +1,7 @@ +package jto.validation +package jsjson +package test + import jto.validation._ import jto.validation.jsjson.Rules._ import jto.validation.jsjson.Writes._ @@ -5,9 +9,9 @@ import org.scalatest._ import scala.scalajs.js import scala.Function.unlift -class FormatSpec extends WordSpec with Matchers with JsAnyEquality { +final class FormatSpec extends WordSpec with Matchers with JsAnyEquality { - case class User(id: Long, name: String) + final case class User(id: Long, name: String) val luigi = User(1, "Luigi") "Format" should { @@ -362,7 +366,7 @@ class FormatSpec extends WordSpec with Matchers with JsAnyEquality { } "format recursive" when { - case class RecUser(name: String, friends: Seq[RecUser] = Nil) + final case class RecUser(name: String, friends: Seq[RecUser] = Nil) val u = RecUser("bob", Seq(RecUser("tom"))) val m = js.Dynamic.literal( @@ -370,7 +374,7 @@ class FormatSpec extends WordSpec with Matchers with JsAnyEquality { "friends" -> js.Array( js.Dynamic.literal("name" -> "tom", "friends" -> js.Array()))) - case class User1(name: String, friend: Option[User1] = None) + final case class User1(name: String, friend: Option[User1] = None) val u1 = User1("bob", Some(User1("tom"))) val m1 = js.Dynamic.literal( "name" -> "bob", "friend" -> js.Dynamic.literal("name" -> "tom")) diff --git a/validation-jsjson/src/test/scala/JsAnyEquality.scala b/validation-jsjson/src/test/scala/JsAnyEquality.scala index f39e803a..8284298a 100644 --- a/validation-jsjson/src/test/scala/JsAnyEquality.scala +++ b/validation-jsjson/src/test/scala/JsAnyEquality.scala @@ -1,10 +1,33 @@ +package jto.validation +package jsjson +package test + import scala.scalajs.js import org.scalatest._ trait JsAnyEquality { this: Matchers => implicit class ShouldBeEqualAfterStringify(val dynamic: js.Any) { + def shouldBe(otherDynamic: js.Any): Assertion = - js.JSON.stringify(dynamic) shouldBe js.JSON.stringify(otherDynamic) + (dynamic, otherDynamic) match { + case (d1 : js.Object, d2: js.Object) => + + def props(d: js.Object): Map[String, js.Any] = + d.asInstanceOf[js.Dictionary[js.Any]].toMap[String, js.Any] + + val props1 = props(d1) + val props2 = props(d2) + + (props1.keySet should ===(props2.keySet)) + for (k <- props1.keySet) props1(k).shouldBe(props2(k)) + succeed + + case _ => + js.JSON.stringify(dynamic) shouldBe js.JSON.stringify(otherDynamic) + } + + + } } diff --git a/validation-jsjson/src/test/scala/RulesSpec.scala b/validation-jsjson/src/test/scala/RulesSpec.scala index 543bd189..840a3111 100644 --- a/validation-jsjson/src/test/scala/RulesSpec.scala +++ b/validation-jsjson/src/test/scala/RulesSpec.scala @@ -1,3 +1,7 @@ +package jto.validation +package jsjson +package test + import jto.validation._ import jto.validation.jsjson._ import jto.validation.jsjson.Rules._ @@ -496,9 +500,9 @@ class RulesSpec extends WordSpec with Matchers { "validate subclasses (and parse the concrete class)" when { - trait A - case class B(foo: Int) extends A - case class C(bar: Int) extends A + sealed trait A + final case class B(foo: Int) extends A + final case class C(bar: Int) extends A val b = js.Dynamic.literal("name" -> "B", "foo" -> 4) val c = js.Dynamic.literal("name" -> "C", "bar" -> 6) diff --git a/validation-jsjson/src/test/scala/WritesSpec.scala b/validation-jsjson/src/test/scala/WritesSpec.scala index 845e8a17..8cc66b9d 100644 --- a/validation-jsjson/src/test/scala/WritesSpec.scala +++ b/validation-jsjson/src/test/scala/WritesSpec.scala @@ -1,3 +1,7 @@ +package jto.validation +package jsjson +package test + import jto.validation._ import jto.validation.jsjson.Writes._ import org.scalatest._ diff --git a/validation-jsonast/js/src/main/scala/Ast.scala b/validation-jsonast/js/src/main/scala/Ast.scala index de15dc5b..c794fd9b 100644 --- a/validation-jsonast/js/src/main/scala/Ast.scala +++ b/validation-jsonast/js/src/main/scala/Ast.scala @@ -7,7 +7,7 @@ import scala.scalajs.js.JSConverters._ object Ast { val to: Write[JValue, js.Dynamic] = Write[JValue, js.Any] { case JNull => null - case JObject (value) => value.mapValues(to.writes).toJSDictionary + case JObject (value) => value.map{case (k,v) => k -> to.writes(v)}.toJSDictionary case JArray (value) => value.map(to.writes).toJSArray case JBoolean(value) => value case JString (value) => value @@ -17,7 +17,7 @@ object Ast { }.map(_.asInstanceOf[js.Dynamic]) private val undefined = scala.scalajs.js.undefined - private case class FunctionInJsonException(path: Path) extends Exception + private final case class FunctionInJsonException(path: Path) extends Exception private def unsafeAny2JValue(input: Any, path: Path): JValue = input match { case null => JNull @@ -27,7 +27,7 @@ object Ast { case `undefined` => JNull case a: js.Array[js.Dynamic @unchecked] => - JArray(a.map(v => unsafeAny2JValue(v, path \ 0))) + JArray(a.map(v => unsafeAny2JValue(v, path \ 0)).toSeq) case o: js.Object => JObject(o.asInstanceOf[js.Dictionary[js.Dynamic]] diff --git a/validation-jsonast/jvm/src/main/scala/Ast.scala b/validation-jsonast/jvm/src/main/scala/Ast.scala index 2644cdb2..56399e1f 100644 --- a/validation-jsonast/jvm/src/main/scala/Ast.scala +++ b/validation-jsonast/jvm/src/main/scala/Ast.scala @@ -6,7 +6,7 @@ import play.api.libs.json._ object Ast { val to: Write[JValue, JsValue] = Write[JValue, JsValue] { case JNull => JsNull - case JObject (value) => JsObject(value.mapValues(to.writes)) + case JObject (value) => JsObject(value.map { case (k,v) => k -> to.writes(v) }) case JArray (value) => JsArray(value.map(to.writes)) case JBoolean(value) => JsBoolean(value) case JString (value) => JsString(value) diff --git a/validation-jsonast/shared/src/main/scala/JValue.scala b/validation-jsonast/shared/src/main/scala/JValue.scala index ee0f0878..454a0a16 100644 --- a/validation-jsonast/shared/src/main/scala/JValue.scala +++ b/validation-jsonast/shared/src/main/scala/JValue.scala @@ -2,12 +2,12 @@ package jto.validation package jsonast sealed trait JValue -case object JNull extends JValue -case class JObject (value: Map[String, JValue] = Map.empty) extends JValue -case class JArray (value: Seq[JValue] = Seq.empty) extends JValue -case class JBoolean(value: Boolean) extends JValue -case class JString (value: String) extends JValue -case class JNumber (value: String) extends JValue { +final case object JNull extends JValue +final case class JObject (value: Map[String, JValue] = Map.empty) extends JValue +final case class JArray (value: Seq[JValue] = Seq.empty) extends JValue +final case class JBoolean(value: Boolean) extends JValue +final case class JString (value: String) extends JValue +final case class JNumber (value: String) extends JValue { require(JNumber.regex.matcher(value).matches) } diff --git a/validation-jsonast/shared/src/main/scala/Writes.scala b/validation-jsonast/shared/src/main/scala/Writes.scala index be7c4a15..f6fbd971 100644 --- a/validation-jsonast/shared/src/main/scala/Writes.scala +++ b/validation-jsonast/shared/src/main/scala/Writes.scala @@ -73,7 +73,7 @@ trait Writes implicit def mapW[I](implicit w: WriteLike[I, JValue]) = Write[Map[String, I], JObject] { m => - JObject(m.mapValues(w.writes)) + JObject(m.map { case (k,v) => k -> w.writes(v) }) } implicit def vaW[I](implicit w: WriteLike[I, JValue]) = diff --git a/validation-jsonast/shared/src/test/scala/AstSpec.scala b/validation-jsonast/shared/src/test/scala/AstSpec.scala index 55b9d17a..79025572 100644 --- a/validation-jsonast/shared/src/test/scala/AstSpec.scala +++ b/validation-jsonast/shared/src/test/scala/AstSpec.scala @@ -1,3 +1,7 @@ +package jto.validation +package jsonast +package test + import jto.validation.Valid import jto.validation.jsonast._ import org.scalatest._ diff --git a/validation-jsonast/shared/src/test/scala/FormatSpec.scala b/validation-jsonast/shared/src/test/scala/FormatSpec.scala index 3629ffac..d430ea71 100644 --- a/validation-jsonast/shared/src/test/scala/FormatSpec.scala +++ b/validation-jsonast/shared/src/test/scala/FormatSpec.scala @@ -1,10 +1,14 @@ +package jto.validation +package jsonast +package test + import jto.validation._ import jto.validation.jsonast._ import org.scalatest._ import scala.Function.unlift class FormatSpec extends WordSpec with Matchers { - case class User(id: Long, name: String) + final case class User(id: Long, name: String) val luigi = User(1, "Luigi") "Format" should { @@ -413,7 +417,7 @@ class FormatSpec extends WordSpec with Matchers { } "format recursive" when { - case class RecUser(name: String, friends: Seq[RecUser] = Nil) + final case class RecUser(name: String, friends: Seq[RecUser] = Nil) val u = RecUser("bob", Seq(RecUser("tom"))) val m = JObject( @@ -421,7 +425,7 @@ class FormatSpec extends WordSpec with Matchers { "friends" -> JArray(Seq(JObject(Map("name" -> JString("tom"), "friends" -> JArray())))))) - case class User1(name: String, friend: Option[User1] = None) + final case class User1(name: String, friend: Option[User1] = None) val u1 = User1("bob", Some(User1("tom"))) val m1 = JObject(Map("name" -> JString("bob"), "friend" -> JObject(Map("name" -> JString("tom"))))) diff --git a/validation-jsonast/shared/src/test/scala/MacroSpec.scala b/validation-jsonast/shared/src/test/scala/MacroSpec.scala index 9ced6b76..49614d6c 100644 --- a/validation-jsonast/shared/src/test/scala/MacroSpec.scala +++ b/validation-jsonast/shared/src/test/scala/MacroSpec.scala @@ -1,29 +1,33 @@ +package jto.validation +package jsonast +package test + import jto.validation._ import jto.validation.jsonast._ import org.scalatest._ -case class User(age: Int, name: String) -case class Dog(name: String, master: User) -case class Cat(name: String) -case class RecUser(name: String, +final case class User(age: Int, name: String) +final case class Dog(name: String, master: User) +final case class Cat(name: String) +final case class RecUser(name: String, cat: Option[Cat] = None, hobbies: Seq[String] = Seq(), friends: Seq[RecUser] = Seq()) -case class User1(name: String, friend: Option[User1] = None) -case class UserMap(name: String, friends: Map[String, UserMap] = Map()) +final case class User1(name: String, friend: Option[User1] = None) +final case class UserMap(name: String, friends: Map[String, UserMap] = Map()) -case class Toto(name: String) -case class Toto2(name: Option[String]) -case class Toto3(name: List[Double]) -case class Toto4(name: Set[Long]) -case class Toto5(name: Map[String, Int]) -case class Toto6(name: Seq[Dog]) -case class UserFail(name: String, bd: Toto) +final case class Toto(name: String) +final case class Toto2(name: Option[String]) +final case class Toto3(name: List[Double]) +final case class Toto4(name: Set[Long]) +final case class Toto5(name: Map[String, Int]) +final case class Toto6(name: Seq[Dog]) +final case class UserFail(name: String, bd: Toto) -case class Id[A](id: A) -case class C1[A](id: Id[A], name: String) +final case class Id[A](id: A) +final case class C1[A](id: Id[A], name: String) -case class X( +final case class X( _1: String, _2: String, _3: String, @@ -47,7 +51,7 @@ case class X( _21: String ) -case class Program(id: Long, +final case class Program(id: Long, name: String, logoPath: Option[String], logoThumb: Option[String]) @@ -55,7 +59,7 @@ object Program { def programs = List.empty[Program] } -case class Person(name: String, age: Int) +final case class Person(name: String, age: Int) object Person { implicit val personRule = { import Rules._ @@ -67,7 +71,7 @@ object Person { } } -case class Person2(names: List[String]) +final case class Person2(names: List[String]) object Person2 { implicit val personRule = { @@ -80,20 +84,20 @@ object Person2 { } } -case class ManyApplies(foo: String, bar: Int) +final case class ManyApplies(foo: String, bar: Int) object ManyApplies { def apply(x: Option[Int]) = 9 def apply(y: String) = 4 def apply(x: String, y: String) = 10 } -trait NotAClass -case class AClass(foo: Int) extends NotAClass +sealed trait NotAClass +final case class AClass(foo: Int) extends NotAClass object NotAClass { def apply(x: Int): NotAClass = AClass(x) } -class MacroSpec extends WordSpec with Matchers { +final class MacroSpec extends WordSpec with Matchers { "MappingMacros" should { "create a Rule[User]" in { diff --git a/validation-jsonast/shared/src/test/scala/RulesSpec.scala b/validation-jsonast/shared/src/test/scala/RulesSpec.scala index 50d70ed4..8352b258 100644 --- a/validation-jsonast/shared/src/test/scala/RulesSpec.scala +++ b/validation-jsonast/shared/src/test/scala/RulesSpec.scala @@ -1,3 +1,7 @@ +package jto.validation +package jsonast +package test + import jto.validation._ import jto.validation.jsonast._ import org.scalatest._ @@ -541,9 +545,9 @@ class RulesSpec extends WordSpec with Matchers { "validate subclasses (and parse the concrete class)" when { - trait A - case class B(foo: Int) extends A - case class C(bar: Int) extends A + sealed trait A + final case class B(foo: Int) extends A + final case class C(bar: Int) extends A val b = JObject(Map("name" -> JString("B"), "foo" -> JNumber(4))) val c = JObject(Map("name" -> JString("C"), "bar" -> JNumber(6))) @@ -594,12 +598,12 @@ class RulesSpec extends WordSpec with Matchers { "perform complex validation" in { - case class Contact(firstname: String, + final case class Contact(firstname: String, lastname: String, company: Option[String], informations: Seq[ContactInformation]) - case class ContactInformation( + final case class ContactInformation( label: String, email: Option[String], phones: Seq[String]) val validJson = JObject( @@ -648,14 +652,14 @@ class RulesSpec extends WordSpec with Matchers { } "read recursive" when { - case class RecUser(name: String, friends: Seq[RecUser] = Nil) + final case class RecUser(name: String, friends: Seq[RecUser] = Nil) val u = RecUser("bob", Seq(RecUser("tom"))) val m = JObject( Map("name" -> JString("bob"), "friends" -> JArray(Seq(JObject(Map("name" -> JString("tom"), "friends" -> JArray())))))) - case class User1(name: String, friend: Option[User1] = None) + final case class User1(name: String, friend: Option[User1] = None) val u1 = User1("bob", Some(User1("tom"))) val m1 = JObject(Map("name" -> JString("bob"), "friend" -> JObject(Map("name" -> JString("tom"))))) diff --git a/validation-jsonast/shared/src/test/scala/WritesSpec.scala b/validation-jsonast/shared/src/test/scala/WritesSpec.scala index 52a6f41e..a737c758 100644 --- a/validation-jsonast/shared/src/test/scala/WritesSpec.scala +++ b/validation-jsonast/shared/src/test/scala/WritesSpec.scala @@ -1,17 +1,21 @@ +package jto.validation +package jsonast +package test + import jto.validation._ import jto.validation.jsonast._ import jto.validation.jsonast.Writes._ import org.scalatest._ import scala.Function.unlift -class WritesSpec extends WordSpec with Matchers { +final class WritesSpec extends WordSpec with Matchers { - case class Contact(firstname: String, + final case class Contact(firstname: String, lastname: String, company: Option[String], informations: Seq[ContactInformation]) - case class ContactInformation( + final case class ContactInformation( label: String, email: Option[String], phones: Seq[String]) val contact = Contact( @@ -286,10 +290,11 @@ class WritesSpec extends WordSpec with Matchers { } "compose" in { - val w = To[JObject] { __ => - ((__ \ "email").write[Option[String]] ~ - (__ \ "phones").write[Seq[String]]).tupled - } + val w: Write[(Option[String], Seq[String]), JObject] = + To[JObject] { __ => + ((__ \ "email").write[Option[String]] ~ + (__ \ "phones").write[Seq[String]]).tupled + } val v = Some("jto@foobar.com") -> Seq("01.23.45.67.89", "98.76.54.32.10") @@ -339,7 +344,7 @@ class WritesSpec extends WordSpec with Matchers { } "write recursive" when { - case class RecUser(name: String, friends: List[RecUser] = Nil) + final case class RecUser(name: String, friends: List[RecUser] = Nil) val u = RecUser("bob", List(RecUser("tom"))) val m = JObject( @@ -347,7 +352,7 @@ class WritesSpec extends WordSpec with Matchers { "friends" -> JArray(Seq(JObject(Map("name" -> JString("tom"), "friends" -> JArray())))))) - case class User1(name: String, friend: Option[User1] = None) + final case class User1(name: String, friend: Option[User1] = None) val u1 = User1("bob", Some(User1("tom"))) val m1 = JObject(Map("name" -> JString("bob"), "friend" -> JObject(Map("name" -> JString("tom"))))) @@ -408,7 +413,7 @@ class WritesSpec extends WordSpec with Matchers { } object TestValueClass { - case class Id(value: String) extends AnyVal + final case class Id(value: String) extends AnyVal object Id { implicit val writes: Write[Id, JString] = Write(id => JString(id.value)) } diff --git a/validation-playjson/src/test/scala/MacroSpec.scala b/validation-playjson/src/test/scala/MacroSpec.scala index 68cc4481..449a3fe4 100644 --- a/validation-playjson/src/test/scala/MacroSpec.scala +++ b/validation-playjson/src/test/scala/MacroSpec.scala @@ -3,28 +3,28 @@ import jto.validation.playjson._ import org.scalatest._ import play.api.libs.json.{JsValue, JsObject, Json} -case class User(age: Int, name: String) -case class Dog(name: String, master: User) -case class Cat(name: String) -case class RecUser(name: String, +final case class User(age: Int, name: String) +final case class Dog(name: String, master: User) +final case class Cat(name: String) +final case class RecUser(name: String, cat: Option[Cat] = None, hobbies: Seq[String] = Seq(), friends: Seq[RecUser] = Seq()) -case class User1(name: String, friend: Option[User1] = None) -case class UserMap(name: String, friends: Map[String, UserMap] = Map()) +final case class User1(name: String, friend: Option[User1] = None) +final case class UserMap(name: String, friends: Map[String, UserMap] = Map()) -case class Toto(name: String) -case class Toto2(name: Option[String]) -case class Toto3(name: List[Double]) -case class Toto4(name: Set[Long]) -case class Toto5(name: Map[String, Int]) -case class Toto6(name: Seq[Dog]) -case class UserFail(name: String, bd: Toto) +final case class Toto(name: String) +final case class Toto2(name: Option[String]) +final case class Toto3(name: List[Double]) +final case class Toto4(name: Set[Long]) +final case class Toto5(name: Map[String, Int]) +final case class Toto6(name: Seq[Dog]) +final case class UserFail(name: String, bd: Toto) -case class Id[A](id: A) -case class C1[A](id: Id[A], name: String) +final case class Id[A](id: A) +final case class C1[A](id: Id[A], name: String) -case class X( +final case class X( _1: String, _2: String, _3: String, @@ -48,7 +48,7 @@ case class X( _21: String ) -case class Program(id: Long, +final case class Program(id: Long, name: String, logoPath: Option[String], logoThumb: Option[String]) @@ -56,7 +56,7 @@ object Program { def programs = List.empty[Program] } -case class Person(name: String, age: Int) +final case class Person(name: String, age: Int) object Person { implicit val personRule = { import Rules._ @@ -68,7 +68,7 @@ object Person { } } -case class Person2(names: List[String]) +final case class Person2(names: List[String]) object Person2 { implicit val personRule = { @@ -81,15 +81,15 @@ object Person2 { } } -case class ManyApplies(foo: String, bar: Int) +final case class ManyApplies(foo: String, bar: Int) object ManyApplies { def apply(x: Option[Int]) = 9 def apply(y: String) = 4 def apply(x: String, y: String) = 10 } -trait NotAClass -case class AClass(foo: Int) extends NotAClass +sealed trait NotAClass +final case class AClass(foo: Int) extends NotAClass object NotAClass { def apply(x: Int): NotAClass = AClass(x) } diff --git a/validation-playjson/src/test/scala/RulesSpec.scala b/validation-playjson/src/test/scala/RulesSpec.scala index a868cbc2..b1d883f0 100644 --- a/validation-playjson/src/test/scala/RulesSpec.scala +++ b/validation-playjson/src/test/scala/RulesSpec.scala @@ -463,9 +463,9 @@ class RulesSpec extends WordSpec with Matchers { "validate subclasses (and parse the concrete class)" when { - trait A - case class B(foo: Int) extends A - case class C(bar: Int) extends A + sealed trait A + final case class B(foo: Int) extends A + final case class C(bar: Int) extends A val b = Json.obj("name" -> "B", "foo" -> 4) val c = Json.obj("name" -> "C", "bar" -> 6) @@ -516,12 +516,12 @@ class RulesSpec extends WordSpec with Matchers { "perform complex validation" in { - case class Contact(firstname: String, + final case class Contact(firstname: String, lastname: String, company: Option[String], informations: Seq[ContactInformation]) - case class ContactInformation( + final case class ContactInformation( label: String, email: Option[String], phones: Seq[String]) val validJson = Json.obj( @@ -568,7 +568,7 @@ class RulesSpec extends WordSpec with Matchers { } "read recursive" when { - case class RecUser(name: String, friends: Seq[RecUser] = Nil) + final case class RecUser(name: String, friends: Seq[RecUser] = Nil) val u = RecUser("bob", Seq(RecUser("tom"))) val m = @@ -576,7 +576,7 @@ class RulesSpec extends WordSpec with Matchers { "friends" -> Seq( Json.obj("name" -> "tom", "friends" -> Seq[JsObject]()))) - case class User1(name: String, friend: Option[User1] = None) + final case class User1(name: String, friend: Option[User1] = None) val u1 = User1("bob", Some(User1("tom"))) val m1 = Json.obj("name" -> "bob", "friend" -> Json.obj("name" -> "tom")) diff --git a/validation-xml/src/test/scala/RulesSpec.scala b/validation-xml/src/test/scala/RulesSpec.scala index c215d64c..45710643 100644 --- a/validation-xml/src/test/scala/RulesSpec.scala +++ b/validation-xml/src/test/scala/RulesSpec.scala @@ -311,9 +311,9 @@ class RulesSpec extends WordSpec with Matchers { "validate subclasses (and parse the concrete class)" when { - trait A - case class B(foo: Int) extends A - case class C(bar: Int) extends A + sealed trait A + final case class B(foo: Int) extends A + final case class C(bar: Int) extends A val b = B4 val c = C6 @@ -366,12 +366,12 @@ class RulesSpec extends WordSpec with Matchers { "perform complex validation" in { - case class Contact(firstname: String, + final case class Contact(firstname: String, lastname: String, company: Option[String], informations: ContactInformation) - case class ContactInformation(label: String, + final case class ContactInformation(label: String, email: Option[String], phones: Seq[String]) @@ -404,7 +404,7 @@ class RulesSpec extends WordSpec with Matchers { } "read recursive" when { - case class RecUser(name: String, friends: Seq[RecUser] = Nil) + final case class RecUser(name: String, friends: Seq[RecUser] = Nil) val u = RecUser("bob", Seq(RecUser("tom"))) val m = @@ -417,7 +417,7 @@ class RulesSpec extends WordSpec with Matchers { - case class User1(name: String, friend: Option[User1] = None) + final case class User1(name: String, friend: Option[User1] = None) val u1 = User1("bob", Some(User1("tom"))) val m1 = diff --git a/validation-xml/src/test/scala/WritesSpec.scala b/validation-xml/src/test/scala/WritesSpec.scala index 38d3b7c9..f64aaace 100644 --- a/validation-xml/src/test/scala/WritesSpec.scala +++ b/validation-xml/src/test/scala/WritesSpec.scala @@ -7,14 +7,14 @@ import scala.Function.unlift class WritesSpec extends WordSpec with Matchers { - case class Contact( + final case class Contact( firstname: String, lastname: String, company: Option[String], informations: Seq[ContactInformation] ) - case class ContactInformation( + final case class ContactInformation( label: String, email: Option[String], phones: Seq[String] @@ -181,13 +181,13 @@ class WritesSpec extends WordSpec with Matchers { } "write recursive" when { - case class RecUser(name: String, friends: Seq[RecUser] = Nil) + final case class RecUser(name: String, friends: Seq[RecUser] = Nil) val u = RecUser("bob", Seq(RecUser("tom"))) val m = bobtom - case class User1(name: String, friend: Option[User1] = None) + final case class User1(name: String, friend: Option[User1] = None) val u1 = User1("bob", Some(User1("tom"))) val m1 = bobtom