Skip to content
This repository has been archived by the owner on Apr 24, 2024. It is now read-only.

Commit

Permalink
! routing: CompletionMagnet: gone, streamlining completion API: accom…
Browse files Browse the repository at this point in the history
…plished

This change comes from the insight that with the new marshalling
infrastructure in place CompletionMagnet doesn't serve any higher
purpose.

By removing the now useless magnet layer composition is reinstantiated and
unification as shown in the accompanying test works (with a little help
from type annotations)
  • Loading branch information
jrudolph committed Oct 16, 2013
1 parent 50128fb commit 7a36de5
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 38 deletions.
Expand Up @@ -116,9 +116,45 @@ object ToResponseMarshaller extends LowPriorityToResponseMarshallerImplicits wit
mb(f(value, contentType), ctx.withContentTypeOverriding(contentType))
}
}
implicit def fromResponse: ToResponseMarshaller[HttpResponse] =
new ToResponseMarshaller[HttpResponse] {
def apply(value: HttpResponse, ctx: ToResponseMarshallingContext): Unit = ctx.marshalTo(value)
}
// No implicit conversion to StatusCode allowed here: in `complete(i: Int)`, `i` shouldn't be marshalled
// as a StatusCode
implicit def fromStatusCode: ToResponseMarshaller[StatusCode] =
new ToResponseMarshaller[StatusCode] {
def apply(value: StatusCode, ctx: ToResponseMarshallingContext): Unit = ctx.marshalTo(HttpResponse(value))
}
implicit def fromStatusCodeAndT[S, T](implicit sConv: S StatusCode, tMarshaller: Marshaller[T]): ToResponseMarshaller[(S, T)] =
new ToResponseMarshaller[(S, T)] {
def apply(value: (S, T), ctx: ToResponseMarshallingContext): Unit =
fromMarshaller(sConv(value._1), Nil).apply(value._2, ctx)
}
implicit def fromStatusCodeAndHeadersAndT[S, T](implicit sConv: S StatusCode, tMarshaller: Marshaller[T]): ToResponseMarshaller[(S, Seq[HttpHeader], T)] =
new ToResponseMarshaller[(S, Seq[HttpHeader], T)] {
def apply(value: (S, Seq[HttpHeader], T), ctx: ToResponseMarshallingContext): Unit =
fromMarshaller(sConv(value._1), value._2).apply(value._3, ctx)
}
}

sealed abstract class LowPriorityToResponseMarshallerImplicits {
implicit def liftMarshaller[T](implicit m: Marshaller[T]): ToResponseMarshaller[T] =
ToResponseMarshaller.fromMarshaller()
}
}

/** Something that can later be marshalled into a response */
trait ToResponseMarshallable {
def marshal(ctx: ToResponseMarshallingContext): Unit
}
object ToResponseMarshallable {
implicit def isMarshallable[T](value: T)(implicit marshaller: ToResponseMarshaller[T]): ToResponseMarshallable =
new ToResponseMarshallable {
def marshal(ctx: ToResponseMarshallingContext): Unit = marshaller(value, ctx)
}
implicit def marshallableIsMarshallable: ToResponseMarshaller[ToResponseMarshallable] =
new ToResponseMarshaller[ToResponseMarshallable] {
def apply(value: ToResponseMarshallable, ctx: ToResponseMarshallingContext): Unit = value.marshal(ctx)
}
}

Expand Up @@ -16,11 +16,12 @@

package spray.routing

import scala.concurrent.Promise
import scala.concurrent.{ Future, Promise }
import spray.http._
import HttpHeaders._
import StatusCodes._
import MediaTypes._
import spray.httpx.marshalling.ToResponseMarshallable

class RouteDirectivesSpec extends RoutingSpec {

Expand Down Expand Up @@ -48,6 +49,40 @@ class RouteDirectivesSpec extends RoutingSpec {
get & complete(Promise.successful(HttpResponse(entity = "yup")).future)
} ~> check { responseAs[String] === "yup" }
}
"allow easy handling of futured ToResponseMarshallers" in {
trait RegistrationStatus
case class Registered(name: String) extends RegistrationStatus
case object AlreadyRegistered extends RegistrationStatus

val route =
get {
path("register" / Segment) { name
def registerUser(name: String): Future[RegistrationStatus] = Future.successful {
name match {
case "otto" AlreadyRegistered
case _ Registered(name)
}
}
complete {
registerUser(name).map[ToResponseMarshallable] {
case Registered(_) HttpData.Empty
case AlreadyRegistered
import spray.json.DefaultJsonProtocol._
import spray.httpx.SprayJsonSupport._
(StatusCodes.BadRequest, Map("error" -> "User already Registered"))
}
}
}
}

Get("/register/otto") ~> route ~> check {
status === StatusCodes.BadRequest
}
Get("/register/karl") ~> route ~> check {
status === StatusCodes.OK
entity === HttpEntity.Empty
}
}
}

"the redirect directive" should {
Expand All @@ -68,4 +103,4 @@ class RouteDirectivesSpec extends RoutingSpec {
}
}

}
}
Expand Up @@ -18,7 +18,7 @@ package spray.routing
package directives

import scala.concurrent.{ ExecutionContext, Future }
import spray.httpx.marshalling.{ Marshaller, ToResponseMarshaller }
import spray.httpx.marshalling.{ ToResponseMarshaller, ToResponseMarshallable }
import spray.http._
import StatusCodes._

Expand Down Expand Up @@ -46,8 +46,8 @@ trait RouteDirectives {
/**
* Completes the request using the given arguments.
*/
def complete: ( CompletionMagnet) StandardRoute = magnet new StandardRoute {
def apply(ctx: RequestContext): Unit = magnet.apply(ctx)
def complete: ( ToResponseMarshallable) StandardRoute = marshallable new StandardRoute {
def apply(ctx: RequestContext): Unit = ctx.complete(marshallable)
}

/**
Expand All @@ -64,35 +64,3 @@ object RouteDirectives extends RouteDirectives {
def apply(ctx: RequestContext): Unit = ctx.reject()
}
}

sealed abstract class CompletionMagnet extends Route

object CompletionMagnet {
implicit def fromObject[T: ToResponseMarshaller](obj: T): CompletionMagnet =
new CompletionRoute(obj)

implicit def fromStatusObject[T: Marshaller](tuple: (StatusCode, T)): CompletionMagnet =
fromStatusHeadersObject((tuple._1, Nil, tuple._2))

implicit def fromStatusHeadersObject[T: Marshaller](tuple: (StatusCode, List[HttpHeader], T)): CompletionMagnet =
new CompletionRoute(tuple._3)(ToResponseMarshaller.fromMarshaller(tuple._1, tuple._2))

implicit def fromHttpResponse(response: HttpResponse): CompletionMagnet =
new CompletionMagnet {
def apply(ctx: RequestContext): Unit = ctx.complete(response)
}
implicit def fromStatus(status: StatusCode): CompletionMagnet =
new CompletionMagnet {
def apply(ctx: RequestContext): Unit = ctx.complete(status)
}
implicit def fromHttpResponseFuture(future: Future[HttpResponse])(implicit ec: ExecutionContext): CompletionMagnet =
new CompletionMagnet {
def apply(ctx: RequestContext): Unit = ctx.complete(future)
}
implicit def fromStatusCodeFuture(future: Future[StatusCode])(implicit ec: ExecutionContext): CompletionMagnet =
future.map(status HttpResponse(status, entity = status.defaultMessage))

private class CompletionRoute[T: ToResponseMarshaller](obj: T) extends CompletionMagnet {
def apply(ctx: RequestContext): Unit = ctx.complete(obj)
}
}

0 comments on commit 7a36de5

Please sign in to comment.