Skip to content

Instantly share code, notes, and snippets.

@derekwyatt
Created March 19, 2013 12:42
Show Gist options
  • Select an option

  • Save derekwyatt/5195781 to your computer and use it in GitHub Desktop.

Select an option

Save derekwyatt/5195781 to your computer and use it in GitHub Desktop.

Revisions

  1. derekwyatt created this gist Mar 19, 2013.
    62 changes: 62 additions & 0 deletions Server.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,62 @@
    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)
    }
    }
    }
    }
    47 changes: 47 additions & 0 deletions ServerSpec.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,47 @@
    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
    }