Skip to content

Instantly share code, notes, and snippets.

@derekwyatt
Created March 19, 2013 12:42
Show Gist options
  • Save derekwyatt/5195781 to your computer and use it in GitHub Desktop.
Save derekwyatt/5195781 to your computer and use it in GitHub Desktop.
Spray routing head scratcher...
package org.derekwyatt
import spray.http._
import spray.http.MediaTypes._
import spray.httpx.encoding._
import spray.httpx.SprayJsonSupport
import spray.json._
import spray.json.DefaultJsonProtocol
import spray.routing.HttpService
object Types {
case class UserId(userId: String)
case class StoredObject(something: String)
object TypesProtocol extends DefaultJsonProtocol {
implicit val storedObjectFormat = jsonFormat1(StoredObject)
}
}
trait Store {
import Types._
def get(userId: UserId): Option[Map[String, StoredObject]]
def put(userId: UserId, obj: StoredObject): Boolean
def delete(userId: UserId): Boolean
}
trait Server extends HttpService with SprayJsonSupport {
import Types.TypesProtocol._
import Types._
def store: Store
val apiRoute = {
path("objects" / PathElement) { userId =>
get {
respondWithMediaType(`application/json`) {
println("AAAA")
store get(UserId(userId)) match {
case Some(objs) =>
complete(objs.toJson.compactPrint)
case None =>
complete(StatusCodes.NotFound)
}
}
} ~
post {
entity(as[StoredObject]) { obj =>
println("BBBB")
store put(UserId(userId), obj)
complete(StatusCodes.Created)
}
} ~
// If the 'ctx' is removed and the 'complete' directive used instead, then
// this delete directive is called on the Post, failing the test with a
// NotImplementedError
delete { ctx =>
println("CCCC")
store delete(UserId(userId))
ctx.complete(StatusCodes.OK)
}
}
}
}
package org.derekwyatt
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import spray.json._
import spray.http.MediaTypes._
import spray.http.{StatusCodes, HttpBody}
import spray.httpx.SprayJsonSupport
import spray.testkit.ScalatestRouteTest
import Types._
class ServerSpec extends WordSpec
with MustMatchers
with ScalatestRouteTest {
import Types.TypesProtocol._
import Types._
trait TwoStore extends Store {
@volatile var map = Map("one" -> StoredObject("one"), "two" -> StoredObject("two"))
println("TwoStore Called")
def get(userId: UserId): Option[Map[String, StoredObject]] = Some(map)
def put(userId: UserId, obj: StoredObject): Boolean =
if (map contains obj.something) {
false
} else {
map += (obj.something -> obj)
true
}
def delete(userId: UserId): Boolean = ???
object store extends TwoStore
}
class TwoServerInstance extends Server with TwoStore {
def actorRefFactory = system
}
val postObject = StoredObject("postObject")
"Server" should { //{1
"allow posting to the objects" in new TwoServerInstance { //{2
Post("/objects/user1234", HttpBody(`application/json`, postObject.toJson.compactPrint)) ~> apiRoute ~> check {
status must be (StatusCodes.Created)
map("postObject") must be (postObject)
}
} //}2
} //}1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment