Skip to content

Commit

Permalink
Merge pull request #342 from RafalSumislawski/scala_2_13
Browse files Browse the repository at this point in the history
Upgrade to scala 2.13 and http4s 0.21.0 (second attempt)
  • Loading branch information
zarthross authored Oct 1, 2019
2 parents f6acd78 + a7e21a9 commit b36692c
Show file tree
Hide file tree
Showing 43 changed files with 276 additions and 199 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: scala
scala:
- 2.11.12
- 2.12.8
- 2.12.10
- 2.13.1
jdk:
- openjdk8
- openjdk11
Expand Down
36 changes: 26 additions & 10 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,31 @@ lazy val rho = project

lazy val `rho-core` = project
.in(file("core"))
.settings(rhoPreviousArtifacts(lastVersion = "0.19.0", "core"))
.settings(buildSettings: _*)
.settings(mimaConfiguration)
.settings(buildSettings ++ Seq(
Compile / unmanagedSourceDirectories ++= {
val baseDir = baseDirectory.value

val mainSrcDir = "src/main/scala"
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, minor)) if minor <= 12 => Seq(baseDir / s"$mainSrcDir-2.12-")
case Some((2, minor)) if minor >= 13 => Seq(baseDir / s"$mainSrcDir-2.13+")
case _ => Nil
}
},
libraryDependencies ++= Seq("org.scala-lang.modules" %% "scala-collection-compat" % "2.0.0")
): _*)

lazy val `rho-hal` = project
.in(file("hal"))
.settings(buildSettings :+ halDeps: _*)
.settings(rhoPreviousArtifacts(lastVersion = "0.19.0", "hal"))
.settings(mimaConfiguration)
.dependsOn(`rho-core`)

lazy val `rho-swagger` = project
.in(file("swagger"))
.settings(buildSettings :+ swaggerDeps: _*)
.settings(rhoPreviousArtifacts(lastVersion = "0.19.0", "swagger"))
.settings(mimaConfiguration)
.dependsOn(`rho-core` % "compile->compile;test->test")

lazy val docs = project
Expand All @@ -46,7 +58,7 @@ lazy val docs = project
version.value,
apiVersion.value
),
scalacOptions in (ScalaUnidoc, unidoc) += "-Ypartial-unification",
scalacOptions in (ScalaUnidoc, unidoc) ++= versionSpecificEnabledFlags(scalaVersion.value),
unidocProjectFilter in (ScalaUnidoc, unidoc) := inProjects(
`rho-core`,
`rho-hal`,
Expand Down Expand Up @@ -77,18 +89,22 @@ lazy val `rho-examples` = project
): _*)
.dependsOn(`rho-swagger`, `rho-hal`)

lazy val compileFlags = Seq(
lazy val compilerFlags = Seq(
"-feature",
"-deprecation",
"-unchecked",
"-language:higherKinds",
"-language:existentials",
"-language:implicitConversions",
"-Ywarn-unused",
"-Ypartial-unification",
"-Xfatal-warnings"
)

def versionSpecificEnabledFlags(version: String) = (CrossVersion.partialVersion(version) match {
case Some((2, 13)) => Seq.empty[String]
case _ => Seq("-Ypartial-unification")
})

/* Don't publish setting */
lazy val dontPublish = packagedArtifacts := Map.empty

Expand All @@ -99,9 +115,9 @@ lazy val license = licenses in ThisBuild := Seq(

lazy val buildSettings = publishing ++
Seq(
scalaVersion := "2.12.8",
crossScalaVersions := Seq(scalaVersion.value, "2.11.12"),
scalacOptions ++= compileFlags,
scalaVersion := "2.13.1",
crossScalaVersions := Seq(scalaVersion.value, "2.12.10"),
scalacOptions := compilerFlags ++ versionSpecificEnabledFlags(scalaVersion.value),
resolvers += Resolver.sonatypeRepo("snapshots"),
fork in run := true,
organization in ThisBuild := "org.http4s",
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala-2.12-/jdk/CollectionConverters.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package scala.jdk

import scala.collection.convert.{DecorateAsJava, DecorateAsScala}

object CollectionConverters extends DecorateAsJava with DecorateAsScala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package scala.collection.compat.view

import scala.collection.generic.CanBuildFrom

class MapViewExtensionMethods[K, V, C <: scala.collection.Map[K, V]](private val self: IterableView[(K, V), C]) extends AnyVal {
def mapValues[W, That](f: V => W)(implicit bf: CanBuildFrom[IterableView[(K, V), C], (K, W), That]): That =
self.map[(K, W), That] { case (k, v) => (k, f(v)) }
}

trait MapViewExtensions {
implicit def toMapViewExtensionMethods[K, V, C <: scala.collection.Map[K, V]](self: IterableView[(K, V), C]): MapViewExtensionMethods[K, V, C] =
new MapViewExtensionMethods[K, V, C](self)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package scala.collection.compat

import scala.collection.{IterableView => SCIterableView}

package object view extends MapViewExtensions {
type IterableView[A, B] = SCIterableView[A, B]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package scala.collection.compat

import scala.collection.{IterableOps, View}

package object view {
type IterableView[A, _] = IterableOps[A, View, View[A]]
}
9 changes: 3 additions & 6 deletions core/src/main/scala/org/http4s/rho/CompileRoutes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ package org.http4s
package rho

import scala.collection.immutable.Seq

import cats.Monad
import cats.data.Kleisli
import cats.effect.Sync
import shapeless.HList

import org.http4s.rho.RhoRoute.Tpe
import org.http4s.rho.bits.PathTree

Expand Down Expand Up @@ -43,8 +40,8 @@ object CompileRoutes {
* @param routes `Seq` of routes to bundle into a service.
* @return An `HttpRoutes`
*/
def foldRoutes[F[_]: Monad](routes: Seq[RhoRoute.Tpe[F]]): HttpRoutes[F] = {
def foldRoutes[F[_]: Sync](routes: Seq[RhoRoute.Tpe[F]]): HttpRoutes[F] = {
val tree = routes.foldLeft(PathTree[F]()){ (t, r) => t.appendRoute(r) }
Kleisli((req: Request[F]) => tree.getResult(req).toResponse)
HttpRoutes((req: Request[F]) => tree.getResult(req).toResponse)
}
}
24 changes: 21 additions & 3 deletions core/src/main/scala/org/http4s/rho/RhoDslPathExtractors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,27 @@ package org.http4s.rho

import org.http4s.rho.bits.PathAST._
import org.http4s.rho.bits._
import org.http4s.rho.RhoDslPathExtractors._
import shapeless.{::, HNil}

import scala.reflect.runtime.universe.TypeTag

trait RhoDslPathExtractors[F[_]] {

private val stringTag = implicitly[TypeTag[String]]

implicit def pathMatch(s: String): TypedPath[F, HNil] = TypedPath(PathMatch(s))

implicit def pathMatch(s: Symbol): TypedPath[F, String :: HNil] =
/**
* Provides 'pathVar syntax for String path variables (Scala 2.12 only)
*/
implicit def pathCapture(s: Symbol): TypedPath[F, String :: HNil] =
TypedPath(PathCapture(s.name, None, StringParser.strParser, stringTag))

/**
* Provides pv"pathVarName" syntax for String path variables as an alternative for 'pathVar (Symbol) syntax which was removed in Scala 2.13.
*/
implicit def pathCapture(sc: StringContext): PathCaptureStringContext[F] =
new PathCaptureStringContext[F](sc)

/**
* Defines a path variable of a URI that should be bound to a route definition
*/
Expand All @@ -34,3 +42,13 @@ trait RhoDslPathExtractors[F[_]] {
TypedPath(PathCapture[F](id, Some(description), parser, stringTag))

}

object RhoDslPathExtractors {

private val stringTag = implicitly[TypeTag[String]]

class PathCaptureStringContext[F[_]](val sc: StringContext) extends AnyVal {
def pv(): TypedPath[F, String :: HNil] =
TypedPath[F, String :: HNil](PathCapture(sc.parts.mkString, None, StringParser.strParser, stringTag))
}
}
5 changes: 2 additions & 3 deletions core/src/main/scala/org/http4s/rho/RhoRoutes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ package org.http4s
package rho

import scala.collection.immutable.Seq

import cats.Monad
import cats.effect.Sync
import org.http4s.rho.bits.PathAST.TypedPath
import org.log4s.getLogger
import shapeless.{HList, HNil}
Expand All @@ -24,7 +23,7 @@ import shapeless.{HList, HNil}
*
* @param routes Routes to prepend before elements in the constructor.
*/
class RhoRoutes[F[_]: Monad](routes: Seq[RhoRoute[F, _ <: HList]] = Vector.empty)
class RhoRoutes[F[_]: Sync](routes: Seq[RhoRoute[F, _ <: HList]] = Vector.empty)
extends bits.MethodAliases
with bits.ResponseGeneratorInstances[F]
with RoutePrependable[F, RhoRoutes[F]]
Expand Down
14 changes: 8 additions & 6 deletions core/src/main/scala/org/http4s/rho/RoutesBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package org.http4s.rho

import scala.collection.immutable.VectorBuilder
import scala.collection.immutable.Seq
import cats.Monad
import cats.effect.Sync
import shapeless.HList
import org.http4s._

import scala.collection.compat._

/** CompileRoutes which accumulates routes and can build a `HttpRoutes` */
final class RoutesBuilder[F[_]: Monad] private(internalRoutes: VectorBuilder[RhoRoute.Tpe[F]]) extends CompileRoutes[F, RhoRoute.Tpe[F]] {
final class RoutesBuilder[F[_]: Sync] private(internalRoutes: VectorBuilder[RhoRoute.Tpe[F]]) extends CompileRoutes[F, RhoRoute.Tpe[F]] {

/** Turn the accumulated routes into an `HttpRoutes`
*
Expand All @@ -25,8 +27,8 @@ final class RoutesBuilder[F[_]: Monad] private(internalRoutes: VectorBuilder[Rho
* @param routes Routes to accumulate.
* @return `this` instance with its internal state mutated.
*/
def append(routes: TraversableOnce[RhoRoute.Tpe[F]]): this.type = {
internalRoutes ++= routes
def append(routes: IterableOnce[RhoRoute.Tpe[F]]): this.type = {
internalRoutes ++= routes.iterator.to(Iterable)
this
}

Expand All @@ -46,10 +48,10 @@ final class RoutesBuilder[F[_]: Monad] private(internalRoutes: VectorBuilder[Rho

object RoutesBuilder {
/** Constructor method for new `RoutesBuilder` instances */
def apply[F[_]: Monad](): RoutesBuilder[F] = apply(Seq.empty)
def apply[F[_]: Sync](): RoutesBuilder[F] = apply(Seq.empty)

/** Constructor method for new `RoutesBuilder` instances with existing routes */
def apply[F[_]: Monad](routes: Seq[RhoRoute.Tpe[F]]): RoutesBuilder[F] = {
def apply[F[_]: Sync](routes: Seq[RhoRoute.Tpe[F]]): RoutesBuilder[F] = {
val builder = new VectorBuilder[RhoRoute.Tpe[F]]
builder ++= routes

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package org.http4s
package rho.bits

import scala.language.higherKinds

import shapeless.HList
import shapeless.ops.hlist.Prepend

Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/org/http4s/rho/bits/PathTree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -258,12 +258,12 @@ private[rho] trait PathTreeOps[F[_]] extends RuleExecutor[F] {

if (!result.isEmpty || end.isEmpty || method == Method.OPTIONS) result
else FailureResponse.pure[F] {
val ms = end.keys
val ms = end.keySet
val allowedMethods = ms.mkString(", ")
val msg = s"$method not allowed. Defined methods: $allowedMethods\n"

F.map(MethodNotAllowed.pure(msg))(
_.putHeaders(headers.Allow(ms.head, ms.tail.toList:_*)))
_.putHeaders(headers.Allow(ms)))
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/org/http4s/rho/bits/QueryParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.http4s.rho.bits.QueryParser.Params

import scala.annotation.tailrec
import scala.collection.immutable.Seq
import scala.collection.generic.CanBuildFrom
import scala.collection.compat._

/** Extract a value from the `Request` `Query`
*
Expand Down Expand Up @@ -41,9 +41,9 @@ trait QueryParsers[F[_]] extends FailureResponseOps[F] {
*
* The elements must have the same name and each be a valid representation of the requisite type.
*/
implicit def multipleParse[A, B[_]](implicit F: Monad[F], p: StringParser[F, A], cbf: CanBuildFrom[Seq[_], A, B[A]]) = new QueryParser[F, B[A]] {
implicit def multipleParse[A, B[_]](implicit F: Monad[F], p: StringParser[F, A], cbf: Factory[A, B[A]]) = new QueryParser[F, B[A]] {
override def collect(name: String, params: Params, default: Option[B[A]]): ResultResponse[F, B[A]] = {
val b = cbf()
val b = cbf.newBuilder
params.get(name) match {
case None => SuccessResponse(default.getOrElse(b.result))
case Some(Seq()) => SuccessResponse(default.getOrElse(b.result))
Expand Down
2 changes: 0 additions & 2 deletions core/src/main/scala/org/http4s/rho/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import org.http4s.rho.bits._
import org.http4s.rho.bits.PathAST._
import shapeless.{HList, HNil}

import scala.language.implicitConversions

package object rho extends org.http4s.syntax.AllSyntax {
type RhoMiddleware[F[_]] = Seq[RhoRoute[F, _ <: HList]] => Seq[RhoRoute[F, _ <: HList]]

Expand Down
8 changes: 4 additions & 4 deletions core/src/test/scala/ApiExamples.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class ApiExamples extends Specification {
GET / "helloworldnumber" / pathVar[Int] / "foo" |>> { i: Int =>
Ok(s"Received $i")
}
// the symbol 'world just says 'capture a String' with variable name "world"
GET / "helloworldstring" / 'world / "foo" |>> { i: String =>
// the pv"world" (pv stands for path variable) says 'capture a String' with variable name "world"
GET / "helloworldstring" / pv"world" / "foo" |>> { i: String =>
Ok(s"Received $i")
}
// capture dates
Expand Down Expand Up @@ -118,7 +118,7 @@ class ApiExamples extends Specification {
val path2 = "two" / pathVar[Int]

val getLength = captureMap(`Content-Length`)(_.length)
val getTag = captureMap(ETag)(_ => -1l)
val getTag = captureMap(ETag)(_ => -1L)

GET / (path1 || path2) +? param[String]("foo") >>> (getLength || getTag) |>> {
(i: Int, foo: String, v: Long) => Ok(s"Received $i, $foo, $v")
Expand All @@ -134,7 +134,7 @@ class ApiExamples extends Specification {
GET / "request" |>> { _: Request[IO] =>
Ok("I don't actually need a request...")
}
GET / "request" / 'foo |>> { (_: Request[IO], _: String) =>
GET / "request" / pv"foo" |>> { (_: Request[IO], _: String) =>
Ok("I wanted a request")
}
}
Expand Down
Loading

0 comments on commit b36692c

Please sign in to comment.