Skip to content

Instantly share code, notes, and snippets.

@pinge
Last active August 29, 2015 14:07
Show Gist options
  • Save pinge/f766773a355c160b99f0 to your computer and use it in GitHub Desktop.
Save pinge/f766773a355c160b99f0 to your computer and use it in GitHub Desktop.
ideas for an api structure with spray

com.compstak.models.Comp

package com.compstak.models

import com.github.nscala_time.time.Imports._

case class Comp(id: Int, createdAt: DateTime)

com.compstak.models.Market

package com.compstak.models

import com.github.nscala_time.time.Imports._

case class Market(id: Int, name: String, createdAt: DateTime)

com.compstak.server.Boot

package com.compstak.server

import akka.actor.{ActorSystem, Props}
import akka.io.IO
import spray.can.Http
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
import com.compstak.api.service.ApiServiceActor

object Boot extends App {

  // we need an ActorSystem to host our application in
  implicit val system = ActorSystem("on-spray-can")

  // create and start our service actor
  val service = system.actorOf(Props[ApiServiceActor], "api-service")

  implicit val timeout = Timeout(5.seconds)
  // start a new HTTP server on port 8080 with our service actor as the handler
  IO(Http) ? Http.Bind(service, interface = "localhost", port = 8080)

}

com.compstak.server.service.PoorService

package com.compstak.server.service

import akka.actor.Actor
import spray.routing._

// this trait defines our service behavior independently from the service actor
trait PoorService extends HttpService

// we don't implement our route structure directly in the service actor because
// we want to be able to test it independently, without having to spin up an actor
abstract class ServiceActor extends Actor with PoorService {

  // the HttpService trait defines only one abstract member, which
  // connects the services environment to the enclosing actor or test
  def actorRefFactory = ???

  // this actor only runs our route, but you could add
  // other things here, like request stream processing
  // or timeout handling
  def receive = ???

}

com.compstak.api.service.ApiService

package com.compstak.api.service

import akka.actor.Actor
import spray.http._
import com.compstak.api._

trait ApiService extends CompsApi with MarketsApi

class ApiServiceActor extends Actor with ApiService {

  def actorRefFactory = context

  def receive = runRoute {
    compsApiRoute ~ marketsApiRoute
  }

}

com.compstak.api.CompsApi.scala

package com.compstak.api

import com.github.nscala_time.time.Imports.DateTime
import play.api.libs.json._
import com.compstak.server.marshalling.JsonSupport
import com.compstak.server.service.PoorService
import com.compstak.models.Comp

trait CompsApi extends PoorService with JsonSupport {

  implicit val compWrites: Writes[Comp] = Json.writes[Comp]

  /*
   * GET    /v1/comps
   * POST   /v1/comps
   * PUT    /v1/comps/:comp_id
   * DELETE /v1/comps/:comp_id
   * PATCH (?)
   *
   */
  val compsApiRoute = {
    pathPrefix("v1" / "comps") {
      pathEnd {
        get {
          complete {
            Seq(new Comp(1, new DateTime))
          }
        } ~
        post {
          respondWithStatus(201) { // 201 Created
            complete {
              new Comp(201, new DateTime)
            }
          }
        }
      } ~
      path(IntNumber) { compId =>
        put {
          complete {
            new Comp(44, new DateTime)
          }
        } ~
        delete {
          complete {
            new Comp(1, new DateTime)
          }
        }
      }
    }
  }

}

com.compstak.api.MarketsApi

package com.compstak.api

import com.github.nscala_time.time.Imports.DateTime
import play.api.libs.json._
import com.compstak.server.service.PoorService
import com.compstak.server.marshalling.JsonSupport
import com.compstak.models.Market

trait MarketsApi extends PoorService with JsonSupport {

  implicit val marketWrites: Writes[Market] = Json.writes[Market]

  /*
   * GET    /v1/markets
   * POST   /v1/markets
   * PUT    /v1/markets/:market_id
   * DELETE /v1/markets/:market_id
   * PATCH (?)
   *
   */
  val marketsApiRoute = {
    pathPrefix("v1" / "markets") {
      pathEnd {
        get {
          complete {
            Seq(new Market(1, "New York City", new DateTime), new Market(2, "Chicago", new DateTime), new Market(3, "Dallas", new DateTime))
          }
        } ~
          post {
            respondWithStatus(201) { // 201 Created
              complete {
                new Market(201, "Bay Area", new DateTime)
              }
            }
          }
      } ~
        path(IntNumber) { marketId =>
          put {
            complete {
              new Market(44, "Minneapolis", new DateTime)
            }
          } ~
            delete {
              complete {
                new Market(1, "Philadelphia", new DateTime)
              }
            }
        }
    }
  }

}

com.compstak.server.marshalling.JsonSupport

package com.compstak.server.marshalling

import spray.httpx.PlayJsonSupport

trait JsonSupport extends PlayJsonSupport // allows switching marshaller at any time
@billoneil
Copy link

I thought spray was supposed to completely hide actors from us?

I really dislike this route syntax we would have to further abstract out some of our own directives.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment