summaryrefslogtreecommitdiff
path: root/cask
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.li@databricks.com>2019-09-14 18:25:51 +0800
committerLi Haoyi <haoyi.li@databricks.com>2019-09-14 18:25:51 +0800
commit2c1dcc3cd33fbd2c2c921f20f67c45ce48c1e8bc (patch)
tree51bc3ee00736d8a55c0515b998f8057d431be018 /cask
parent4e853a9d5b9563dbe1909757bf4be4d8e7d2b36a (diff)
downloadcask-2c1dcc3cd33fbd2c2c921f20f67c45ce48c1e8bc.tar.gz
cask-2c1dcc3cd33fbd2c2c921f20f67c45ce48c1e8bc.tar.bz2
cask-2c1dcc3cd33fbd2c2c921f20f67c45ce48c1e8bc.zip
`cask.Response` is now covariant
Diffstat (limited to 'cask')
-rw-r--r--cask/src/cask/endpoints/JsonEndpoint.scala13
-rw-r--r--cask/src/cask/internal/Conversion.scala9
-rw-r--r--cask/src/cask/main/Decorators.scala7
-rw-r--r--cask/src/cask/model/Response.scala31
4 files changed, 41 insertions, 19 deletions
diff --git a/cask/src/cask/endpoints/JsonEndpoint.scala b/cask/src/cask/endpoints/JsonEndpoint.scala
index edf0c46..3b3b095 100644
--- a/cask/src/cask/endpoints/JsonEndpoint.scala
+++ b/cask/src/cask/endpoints/JsonEndpoint.scala
@@ -4,7 +4,9 @@ import java.io.{ByteArrayOutputStream, InputStream, OutputStream, OutputStreamWr
import cask.internal.{Router, Util}
import cask.main.Endpoint
+import cask.model.Response.DataCompanion
import cask.model.{Request, Response}
+
import collection.JavaConverters._
sealed trait JsReader[T] extends Router.ArgReader[ujson.Value, T, cask.model.Request]
@@ -27,7 +29,7 @@ object JsReader{
}
}
trait JsonData extends Response.Data
-object JsonData{
+object JsonData extends DataCompanion[JsonData]{
implicit class JsonDataImpl[T: upickle.default.Writer](t: T) extends JsonData{
def write(out: OutputStream) = {
val writer = new OutputStreamWriter(out)
@@ -71,7 +73,7 @@ class postJson(val path: String, override val subpath: Boolean = false) extends
} yield obj.toMap
obj match{
case Left(r) => Router.Result.Success(r.map(Response.Data.StringData))
- case Right(params) => delegate(params).map(_.data)
+ case Right(params) => delegate(params)
}
}
def wrapPathSegment(s: String): Input = ujson.Str(s)
@@ -83,12 +85,9 @@ class getJson(val path: String, override val subpath: Boolean = false) extends E
type Input = Seq[String]
type InputParser[T] = QueryParamReader[T]
override type OuterReturned = Router.Result[Response.Raw]
- def wrapFunction(ctx: Request,
- delegate: Delegate): Router.Result[Response.Raw] = {
-
- val res = delegate(WebEndpoint.buildMapFromQueryParams(ctx))
+ def wrapFunction(ctx: Request, delegate: Delegate): Router.Result[Response.Raw] = {
- res.map(_.data)
+ delegate(WebEndpoint.buildMapFromQueryParams(ctx))
}
def wrapPathSegment(s: String) = Seq(s)
} \ No newline at end of file
diff --git a/cask/src/cask/internal/Conversion.scala b/cask/src/cask/internal/Conversion.scala
new file mode 100644
index 0000000..e6a5a47
--- /dev/null
+++ b/cask/src/cask/internal/Conversion.scala
@@ -0,0 +1,9 @@
+package cask.internal
+
+import scala.annotation.implicitNotFound
+
+@implicitNotFound("Cannot return ${T} as a ${V} response")
+class Conversion[T, V](val f: T => V)
+object Conversion{
+ def create[T, V](implicit f: T => V) = new Conversion(f)
+}
diff --git a/cask/src/cask/main/Decorators.scala b/cask/src/cask/main/Decorators.scala
index d2fc0c7..28d44f5 100644
--- a/cask/src/cask/main/Decorators.scala
+++ b/cask/src/cask/main/Decorators.scala
@@ -1,6 +1,6 @@
package cask.main
-import cask.internal.Router
+import cask.internal.{Conversion, Router}
import cask.internal.Router.ArgReader
import cask.model.{Request, Response}
@@ -36,7 +36,10 @@ trait BaseEndpoint extends BaseDecorator{
*/
def subpath: Boolean = false
- def convertToResultType(t: InnerReturned): InnerReturned = t
+ def convertToResultType[T](t: T)
+ (implicit f: Conversion[T, InnerReturned]): InnerReturned = {
+ f.f(t)
+ }
/**
* [[Endpoint]]s are unique among decorators in that they alone can bind
diff --git a/cask/src/cask/model/Response.scala b/cask/src/cask/model/Response.scala
index 5b51689..e9ca672 100644
--- a/cask/src/cask/model/Response.scala
+++ b/cask/src/cask/model/Response.scala
@@ -12,7 +12,7 @@ import cask.internal.Util
* bytes, uPickle JSON-convertable types or arbitrary input streams. You can
* also construct your own implementations of `Response.Data`.
*/
-case class Response[T](
+case class Response[+T](
data: T,
statusCode: Int,
headers: Seq[(String, String)],
@@ -20,23 +20,33 @@ case class Response[T](
){
def map[V](f: T => V) = new Response(f(data), statusCode, headers, cookies)
}
-object Response{
+
+object Response {
type Raw = Response[Data]
def apply[T](data: T,
statusCode: Int = 200,
headers: Seq[(String, String)] = Nil,
cookies: Seq[Cookie] = Nil) = new Response(data, statusCode, headers, cookies)
-
- implicit def dataResponse[T, V](t: T)(implicit c: T => V): Response[V] = {
- Response[V](t)
- }
- implicit def dataResponse2[T, V](t: Response[T])(implicit c: T => V): Response[V] = {
- t.map(c)
- }
trait Data{
def write(out: OutputStream): Unit
}
- object Data{
+ trait DataCompanion[V]{
+ // Put the implicit constructors for Response[Data] into the `Data` companion
+ // object and all subclasses of `Data`, because for some reason putting them in
+ // the `Response` companion object doesn't work properly. For the same unknown
+ // reasons, we cannot have `dataResponse` and `dataResponse2` take two type
+ // params T and V, and instead have to embed the implicit target type as a
+ // parameter of the enclosing trait
+
+ implicit def dataResponse[T](t: T)(implicit c: T => V): Response[V] = {
+ Response(c(t))
+ }
+
+ implicit def dataResponse2[T](t: Response[T])(implicit c: T => V): Response[V] = {
+ t.map(c(_))
+ }
+ }
+ object Data extends DataCompanion[Data]{
implicit class UnitData(s: Unit) extends Data{
def write(out: OutputStream) = ()
}
@@ -57,6 +67,7 @@ object Response{
}
}
}
+
object Redirect{
def apply(url: String) = Response("", 301, Seq("Location" -> url), Nil)
}