From 4e853a9d5b9563dbe1909757bf4be4d8e7d2b36a Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sat, 14 Sep 2019 16:45:39 +0800 Subject: . --- example/compress/app/test/src/ExampleTests.scala | 4 ++-- example/compress/build.sc | 2 +- example/compress2/app/test/src/ExampleTests.scala | 4 ++-- example/compress2/build.sc | 2 +- example/compress3/app/test/src/ExampleTests.scala | 4 ++-- example/compress3/build.sc | 2 +- example/cookies/app/test/src/ExampleTests.scala | 4 ++-- example/cookies/build.sc | 2 +- example/decorated/app/src/Decorated.scala | 4 ++-- example/decorated/app/test/src/ExampleTests.scala | 4 ++-- example/decorated/build.sc | 2 +- example/decorated2/app/src/Decorated2.scala | 4 ++-- example/decorated2/app/test/src/ExampleTests.scala | 4 ++-- example/decorated2/build.sc | 2 +- example/endpoints/app/src/Endpoints.scala | 3 ++- example/endpoints/app/test/src/ExampleTests.scala | 4 ++-- example/endpoints/build.sc | 2 +- example/formJsonPost/app/test/src/ExampleTests.scala | 18 +++++++++--------- example/formJsonPost/build.sc | 2 +- example/httpMethods/app/test/src/ExampleTests.scala | 4 ++-- example/httpMethods/build.sc | 2 +- .../minimalApplication/app/test/src/ExampleTests.scala | 4 ++-- example/minimalApplication/build.sc | 2 +- .../app/test/src/ExampleTests.scala | 4 ++-- example/minimalApplication2/build.sc | 2 +- example/redirectAbort/app/test/src/ExampleTests.scala | 4 ++-- example/redirectAbort/build.sc | 2 +- example/scalatags/app/test/src/ExampleTests.scala | 4 ++-- example/scalatags/build.sc | 2 +- example/staticFiles/app/test/src/ExampleTests.scala | 4 ++-- example/staticFiles/build.sc | 2 +- example/todo/app/src/TodoServer.scala | 2 +- example/todo/app/test/src/ExampleTests.scala | 4 ++-- example/todo/build.sc | 2 +- example/todoApi/app/test/src/ExampleTests.scala | 4 ++-- example/todoApi/build.sc | 2 +- example/todoDb/app/src/TodoMvcDb.scala | 2 +- example/todoDb/app/test/src/ExampleTests.scala | 4 ++-- example/todoDb/build.sc | 2 +- example/twirl/app/test/src/ExampleTests.scala | 4 ++-- example/twirl/build.sc | 2 +- example/variableRoutes/app/test/src/ExampleTests.scala | 4 ++-- example/variableRoutes/build.sc | 2 +- example/websockets/app/test/src/ExampleTests.scala | 6 +++--- example/websockets/build.sc | 2 +- 45 files changed, 76 insertions(+), 75 deletions(-) (limited to 'example') diff --git a/example/compress/app/test/src/ExampleTests.scala b/example/compress/app/test/src/ExampleTests.scala index 5a4a5bf..cd4d8d0 100644 --- a/example/compress/app/test/src/ExampleTests.scala +++ b/example/compress/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'Compress - test(Compress){ host => + test("Compress") - withServer(Compress){ host => val expected = "Hello World! Hello World! Hello World!" requests.get(s"$host").text() ==> expected assert( diff --git a/example/compress/build.sc b/example/compress/build.sc index ac96e15..5e00fc0 100644 --- a/example/compress/build.sc +++ b/example/compress/build.sc @@ -10,7 +10,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/compress2/app/test/src/ExampleTests.scala b/example/compress2/app/test/src/ExampleTests.scala index cbe3301..9f9b4b3 100644 --- a/example/compress2/app/test/src/ExampleTests.scala +++ b/example/compress2/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'Compress2Main - test(Compress2Main) { host => + test("Compress2Main") - withServer(Compress2Main) { host => val expected = "Hello World! Hello World! Hello World!" requests.get(s"$host").text() ==> expected assert( diff --git a/example/compress2/build.sc b/example/compress2/build.sc index c0e75a7..e4c2108 100644 --- a/example/compress2/build.sc +++ b/example/compress2/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/compress3/app/test/src/ExampleTests.scala b/example/compress3/app/test/src/ExampleTests.scala index 3b013f8..88e1cbf 100644 --- a/example/compress3/app/test/src/ExampleTests.scala +++ b/example/compress3/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'Compress3Main - test(Compress3Main){ host => + test("Compress3Main") - withServer(Compress3Main){ host => val expected = "Hello World! Hello World! Hello World!" requests.get(s"$host").text() ==> expected assert( diff --git a/example/compress3/build.sc b/example/compress3/build.sc index c0e75a7..e4c2108 100644 --- a/example/compress3/build.sc +++ b/example/compress3/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/cookies/app/test/src/ExampleTests.scala b/example/cookies/app/test/src/ExampleTests.scala index 951728b..150f59a 100644 --- a/example/cookies/app/test/src/ExampleTests.scala +++ b/example/cookies/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'Cookies - test(Cookies){ host => + test("Cookies") - withServer(Cookies){ host => val sess = requests.Session() sess.get(s"$host/read-cookie").statusCode ==> 400 sess.get(s"$host/store-cookie") diff --git a/example/cookies/build.sc b/example/cookies/build.sc index c0e75a7..e4c2108 100644 --- a/example/cookies/build.sc +++ b/example/cookies/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/decorated/app/src/Decorated.scala b/example/decorated/app/src/Decorated.scala index f037c6a..b3f1149 100644 --- a/example/decorated/app/src/Decorated.scala +++ b/example/decorated/app/src/Decorated.scala @@ -4,12 +4,12 @@ object Decorated extends cask.MainRoutes{ override def toString = "[haoyi]" } class loggedIn extends cask.Decorator { - def wrapFunction(ctx: cask.Request, delegate: Delegate): Returned = { + def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map("user" -> new User())) } } class withExtra extends cask.Decorator { - def wrapFunction(ctx: cask.Request, delegate: Delegate): Returned = { + def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map("extra" -> 31337)) } } diff --git a/example/decorated/app/test/src/ExampleTests.scala b/example/decorated/app/test/src/ExampleTests.scala index 9aea3bc..c2c19a6 100644 --- a/example/decorated/app/test/src/ExampleTests.scala +++ b/example/decorated/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'Decorated - test(Decorated){ host => + test("Decorated") - withServer(Decorated){ host => requests.get(s"$host/hello/woo").text() ==> "woo31337" requests.get(s"$host/internal/boo").text() ==> "boo[haoyi]" requests.get(s"$host/internal-extra/goo").text() ==> "goo[haoyi]31337" diff --git a/example/decorated/build.sc b/example/decorated/build.sc index c0e75a7..e4c2108 100644 --- a/example/decorated/build.sc +++ b/example/decorated/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/decorated2/app/src/Decorated2.scala b/example/decorated2/app/src/Decorated2.scala index 20526b7..fa4b1c5 100644 --- a/example/decorated2/app/src/Decorated2.scala +++ b/example/decorated2/app/src/Decorated2.scala @@ -4,12 +4,12 @@ object Decorated2 extends cask.MainRoutes{ override def toString = "[haoyi]" } class loggedIn extends cask.Decorator { - def wrapFunction(ctx: cask.Request, delegate: Delegate): Returned = { + def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map("user" -> new User())) } } class withExtra extends cask.Decorator { - def wrapFunction(ctx: cask.Request, delegate: Delegate): Returned = { + def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map("extra" -> 31337)) } } diff --git a/example/decorated2/app/test/src/ExampleTests.scala b/example/decorated2/app/test/src/ExampleTests.scala index 7fec82a..4343097 100644 --- a/example/decorated2/app/test/src/ExampleTests.scala +++ b/example/decorated2/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'Decorated2 - test(Decorated2){ host => + test("Decorated2") - withServer(Decorated2){ host => requests.get(s"$host/hello/woo").text() ==> "woo31337" requests.get(s"$host/internal-extra/goo").text() ==> "goo[haoyi]31337" requests.get(s"$host/ignore-extra/boo").text() ==> "boo[haoyi]" diff --git a/example/decorated2/build.sc b/example/decorated2/build.sc index c0e75a7..e4c2108 100644 --- a/example/decorated2/build.sc +++ b/example/decorated2/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/endpoints/app/src/Endpoints.scala b/example/endpoints/app/src/Endpoints.scala index b478c80..b934769 100644 --- a/example/endpoints/app/src/Endpoints.scala +++ b/example/endpoints/app/src/Endpoints.scala @@ -2,7 +2,8 @@ package app class custom(val path: String, val methods: Seq[String]) extends cask.Endpoint{ type Output = Int - def wrapFunction(ctx: cask.Request, delegate: Delegate): Returned = { + type InnerReturned = Int + def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map()).map{num => cask.Response("Echo " + num, statusCode = num) } diff --git a/example/endpoints/app/test/src/ExampleTests.scala b/example/endpoints/app/test/src/ExampleTests.scala index 1302d85..3314f24 100644 --- a/example/endpoints/app/test/src/ExampleTests.scala +++ b/example/endpoints/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'Endpoints - test(Endpoints){ host => + test("Endpoints") - withServer(Endpoints){ host => requests.get(s"$host/echo/200").text() ==> "Echo 200" requests.get(s"$host/echo/200").statusCode ==> 200 requests.get(s"$host/echo/400").text() ==> "Echo 400" diff --git a/example/endpoints/build.sc b/example/endpoints/build.sc index c0e75a7..e4c2108 100644 --- a/example/endpoints/build.sc +++ b/example/endpoints/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/formJsonPost/app/test/src/ExampleTests.scala b/example/formJsonPost/app/test/src/ExampleTests.scala index 148ebfd..4178497 100644 --- a/example/formJsonPost/app/test/src/ExampleTests.scala +++ b/example/formJsonPost/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,23 +17,23 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'FormJsonPost - test(FormJsonPost){ host => - requests.post(s"$host/json", data = """{"value1": true, "value2": [3]}""").text() ==> - "OK true List(3)" + test("FormJsonPost") - withServer(FormJsonPost){ host => + val response1 = requests.post(s"$host/json", data = """{"value1": true, "value2": [3]}""") + ujson.read(response1.text()) ==> ujson.Str("OK true List(3)") - requests.post( + val response2 = requests.post( s"$host/form", data = Seq("value1" -> "hello", "value2" -> "1", "value2" -> "2") - ).text() ==> - "OK FormValue(hello,null) List(1, 2)" + ) + response2.text() ==> "OK FormValue(hello,null) List(1, 2)" - val resp = requests.post( + val response3 = requests.post( s"$host/upload", data = requests.MultiPart( requests.MultiItem("image", "...", "my-best-image.txt") ) ) - resp.text() ==> "my-best-image.txt" + response3.text() ==> "my-best-image.txt" } } } diff --git a/example/formJsonPost/build.sc b/example/formJsonPost/build.sc index c0e75a7..e4c2108 100644 --- a/example/formJsonPost/build.sc +++ b/example/formJsonPost/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/httpMethods/app/test/src/ExampleTests.scala b/example/httpMethods/app/test/src/ExampleTests.scala index e14bcf5..30fa87f 100644 --- a/example/httpMethods/app/test/src/ExampleTests.scala +++ b/example/httpMethods/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'HttpMethods - test(HttpMethods){ host => + test("HttpMethods") - withServer(HttpMethods){ host => requests.post(s"$host/login").text() ==> "do_the_login" requests.get(s"$host/login").text() ==> "show_the_login_form" } diff --git a/example/httpMethods/build.sc b/example/httpMethods/build.sc index c0e75a7..e4c2108 100644 --- a/example/httpMethods/build.sc +++ b/example/httpMethods/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/minimalApplication/app/test/src/ExampleTests.scala b/example/minimalApplication/app/test/src/ExampleTests.scala index 8c8ecb2..1cda7a6 100644 --- a/example/minimalApplication/app/test/src/ExampleTests.scala +++ b/example/minimalApplication/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests { - 'MinimalApplication - test(MinimalApplication) { host => + test("MinimalApplication") - withServer(MinimalApplication) { host => val success = requests.get(host) success.text() ==> "Hello World!" diff --git a/example/minimalApplication/build.sc b/example/minimalApplication/build.sc index c0e75a7..e4c2108 100644 --- a/example/minimalApplication/build.sc +++ b/example/minimalApplication/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/minimalApplication2/app/test/src/ExampleTests.scala b/example/minimalApplication2/app/test/src/ExampleTests.scala index 0d8f1bc..4e5621c 100644 --- a/example/minimalApplication2/app/test/src/ExampleTests.scala +++ b/example/minimalApplication2/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'MinimalApplication2 - test(MinimalMain){ host => + test("MinimalApplication2") - withServer(MinimalMain){ host => val success = requests.get(host) success.text() ==> "Hello World!" diff --git a/example/minimalApplication2/build.sc b/example/minimalApplication2/build.sc index c0e75a7..e4c2108 100644 --- a/example/minimalApplication2/build.sc +++ b/example/minimalApplication2/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/redirectAbort/app/test/src/ExampleTests.scala b/example/redirectAbort/app/test/src/ExampleTests.scala index f095517..a4d149f 100644 --- a/example/redirectAbort/app/test/src/ExampleTests.scala +++ b/example/redirectAbort/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -18,7 +18,7 @@ object ExampleTests extends TestSuite{ val tests = Tests{ - 'RedirectAbort - test(RedirectAbort){ host => + test("RedirectAbort") - withServer(RedirectAbort){ host => val resp = requests.get(s"$host/") resp.statusCode ==> 401 resp.history.get.statusCode ==> 301 diff --git a/example/redirectAbort/build.sc b/example/redirectAbort/build.sc index c0e75a7..e4c2108 100644 --- a/example/redirectAbort/build.sc +++ b/example/redirectAbort/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/scalatags/app/test/src/ExampleTests.scala b/example/scalatags/app/test/src/ExampleTests.scala index 8eccecf..53bc1ea 100644 --- a/example/scalatags/app/test/src/ExampleTests.scala +++ b/example/scalatags/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests { - 'Scalatags - test(Scalatags) { host => + test("Scalatags") - withServer(Scalatags) { host => val body = requests.get(host).text() assert( diff --git a/example/scalatags/build.sc b/example/scalatags/build.sc index 3971d88..e95569a 100644 --- a/example/scalatags/build.sc +++ b/example/scalatags/build.sc @@ -11,7 +11,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/staticFiles/app/test/src/ExampleTests.scala b/example/staticFiles/app/test/src/ExampleTests.scala index ab67bcf..ebda8a0 100644 --- a/example/staticFiles/app/test/src/ExampleTests.scala +++ b/example/staticFiles/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -18,7 +18,7 @@ object ExampleTests extends TestSuite{ val tests = Tests{ - 'StaticFiles - test(StaticFiles){ host => + test("StaticFiles") - withServer(StaticFiles){ host => requests.get(s"$host/static/file/example.txt").text() ==> "the quick brown fox jumps over the lazy dog" diff --git a/example/staticFiles/build.sc b/example/staticFiles/build.sc index d45045d..e0f8519 100644 --- a/example/staticFiles/build.sc +++ b/example/staticFiles/build.sc @@ -11,7 +11,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) diff --git a/example/todo/app/src/TodoServer.scala b/example/todo/app/src/TodoServer.scala index 4adb55c..1a58d09 100644 --- a/example/todo/app/src/TodoServer.scala +++ b/example/todo/app/src/TodoServer.scala @@ -17,7 +17,7 @@ object TodoServer extends cask.MainRoutes{ class transactional extends cask.Decorator{ class TransactionFailed(val value: Router.Result.Error) extends Exception - def wrapFunction(pctx: cask.Request, delegate: Delegate): Returned = { + def wrapFunction(pctx: cask.Request, delegate: Delegate): OuterReturned = { try ctx.transaction( delegate(Map()) match{ case Router.Result.Success(t) => Router.Result.Success(t) diff --git a/example/todo/app/test/src/ExampleTests.scala b/example/todo/app/test/src/ExampleTests.scala index e8ca7eb..e1be23c 100644 --- a/example/todo/app/test/src/ExampleTests.scala +++ b/example/todo/app/test/src/ExampleTests.scala @@ -1,7 +1,7 @@ package app import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = io.undertow.Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -13,7 +13,7 @@ object ExampleTests extends TestSuite{ res } val tests = Tests{ - 'TodoServer - test(TodoServer){ host => + test("TodoServer") - withServer(TodoServer){ host => val page = requests.get(host).text() assert(page.contains("What needs to be done?")) } diff --git a/example/todo/build.sc b/example/todo/build.sc index beeb947..c5d5610 100644 --- a/example/todo/build.sc +++ b/example/todo/build.sc @@ -13,7 +13,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/todoApi/app/test/src/ExampleTests.scala b/example/todoApi/app/test/src/ExampleTests.scala index 5e9e11a..4e85a8e 100644 --- a/example/todoApi/app/test/src/ExampleTests.scala +++ b/example/todoApi/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'TodoMvcApi - test(TodoMvcApi){ host => + test("TodoMvcApi") - withServer(TodoMvcApi){ host => requests.get(s"$host/list/all").text() ==> """[{"checked":true,"text":"Get started with Cask"},{"checked":false,"text":"Profit!"}]""" requests.get(s"$host/list/active").text() ==> diff --git a/example/todoApi/build.sc b/example/todoApi/build.sc index c0e75a7..e4c2108 100644 --- a/example/todoApi/build.sc +++ b/example/todoApi/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/todoDb/app/src/TodoMvcDb.scala b/example/todoDb/app/src/TodoMvcDb.scala index 7ce3c50..669b2b8 100644 --- a/example/todoDb/app/src/TodoMvcDb.scala +++ b/example/todoDb/app/src/TodoMvcDb.scala @@ -16,7 +16,7 @@ object TodoMvcDb extends cask.MainRoutes{ class transactional extends cask.Decorator{ class TransactionFailed(val value: Router.Result.Error) extends Exception - def wrapFunction(pctx: cask.Request, delegate: Delegate): Returned = { + def wrapFunction(pctx: cask.Request, delegate: Delegate): OuterReturned = { try ctx.transaction( delegate(Map()) match{ case Router.Result.Success(t) => Router.Result.Success(t) diff --git a/example/todoDb/app/test/src/ExampleTests.scala b/example/todoDb/app/test/src/ExampleTests.scala index eccd913..77cbbb3 100644 --- a/example/todoDb/app/test/src/ExampleTests.scala +++ b/example/todoDb/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'TodoMvcDb - test(TodoMvcDb){ host => + test("TodoMvcDb") - withServer(TodoMvcDb){ host => requests.get(s"$host/list/all").text() ==> """[{"id":1,"checked":true,"text":"Get started with Cask"},{"id":2,"checked":false,"text":"Profit!"}]""" requests.get(s"$host/list/active").text() ==> diff --git a/example/todoDb/build.sc b/example/todoDb/build.sc index e2afdfc..589af39 100644 --- a/example/todoDb/build.sc +++ b/example/todoDb/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/twirl/app/test/src/ExampleTests.scala b/example/twirl/app/test/src/ExampleTests.scala index 815f656..2c445fb 100644 --- a/example/twirl/app/test/src/ExampleTests.scala +++ b/example/twirl/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests { - 'Twirl - test(Twirl) { host => + test("Twirl") - withServer(Twirl) { host => val body = requests.get(host).text() assert( diff --git a/example/twirl/build.sc b/example/twirl/build.sc index 6a59f0d..de9fc20 100644 --- a/example/twirl/build.sc +++ b/example/twirl/build.sc @@ -15,7 +15,7 @@ trait AppModule extends ScalaModule with mill.twirllib.TwirlModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/variableRoutes/app/test/src/ExampleTests.scala b/example/variableRoutes/app/test/src/ExampleTests.scala index 48455fa..1755dea 100644 --- a/example/variableRoutes/app/test/src/ExampleTests.scala +++ b/example/variableRoutes/app/test/src/ExampleTests.scala @@ -4,7 +4,7 @@ import io.undertow.Undertow import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'VariableRoutes - test(VariableRoutes){ host => + test("VariableRoutes") - withServer(VariableRoutes){ host => val noIndexPage = requests.get(host) noIndexPage.statusCode ==> 404 diff --git a/example/variableRoutes/build.sc b/example/variableRoutes/build.sc index c0e75a7..e4c2108 100644 --- a/example/variableRoutes/build.sc +++ b/example/variableRoutes/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ) } diff --git a/example/websockets/app/test/src/ExampleTests.scala b/example/websockets/app/test/src/ExampleTests.scala index 896c051..a463824 100644 --- a/example/websockets/app/test/src/ExampleTests.scala +++ b/example/websockets/app/test/src/ExampleTests.scala @@ -8,7 +8,7 @@ import utest._ object ExampleTests extends TestSuite{ - def test[T](example: cask.main.BaseMain)(f: String => T): T = { + def withServer[T](example: cask.main.BaseMain)(f: String => T): T = { val server = io.undertow.Undertow.builder .addHttpListener(8080, "localhost") .setHandler(example.defaultHandler) @@ -21,7 +21,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests{ - 'Websockets - test(Websockets){ host => + test("Websockets") - withServer(Websockets){ host => @volatile var out = List.empty[String] val client = org.asynchttpclient.Dsl.asyncHttpClient(); try{ @@ -72,7 +72,7 @@ object ExampleTests extends TestSuite{ } } - 'Websockets2000 - test(Websockets){ host => + test("Websockets2000") - withServer(Websockets){ host => @volatile var out = List.empty[String] val closed = new AtomicInteger(0) val client = org.asynchttpclient.Dsl.asyncHttpClient(); diff --git a/example/websockets/build.sc b/example/websockets/build.sc index c788216..197e285 100644 --- a/example/websockets/build.sc +++ b/example/websockets/build.sc @@ -9,7 +9,7 @@ trait AppModule extends ScalaModule{ def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( - ivy"com.lihaoyi::utest::0.6.9", + ivy"com.lihaoyi::utest::0.7.1", ivy"com.lihaoyi::requests::0.2.0", ivy"org.asynchttpclient:async-http-client:2.5.2" ) -- cgit v1.2.3 From 2c1dcc3cd33fbd2c2c921f20f67c45ce48c1e8bc Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sat, 14 Sep 2019 18:25:51 +0800 Subject: `cask.Response` is now covariant --- cask/src/cask/endpoints/JsonEndpoint.scala | 13 +++++------ cask/src/cask/internal/Conversion.scala | 9 +++++++ cask/src/cask/main/Decorators.scala | 7 ++++-- cask/src/cask/model/Response.scala | 31 +++++++++++++++++-------- example/formJsonPost/app/src/FormJsonPost.scala | 3 +++ 5 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 cask/src/cask/internal/Conversion.scala (limited to 'example') 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) } diff --git a/example/formJsonPost/app/src/FormJsonPost.scala b/example/formJsonPost/app/src/FormJsonPost.scala index 3714f39..6dc82d3 100644 --- a/example/formJsonPost/app/src/FormJsonPost.scala +++ b/example/formJsonPost/app/src/FormJsonPost.scala @@ -1,4 +1,7 @@ package app + +import java.io.ByteArrayInputStream + object FormJsonPost extends cask.MainRoutes{ @cask.postJson("/json") def jsonEndpoint(value1: ujson.Js.Value, value2: Seq[Int]) = { -- cgit v1.2.3 From 85e982a6bf9bd82524baf53546b31d85b426fa62 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sat, 14 Sep 2019 18:34:57 +0800 Subject: `InnerResponse` is now a type param instead of member, allowing better error messages through `cask.internal.Conversion`: ``` Cannot return java.io.ByteArrayInputStream as a cask.model.Response[cask.endpoints.JsonData] ``` --- cask/src/cask/endpoints/FormEndpoint.scala | 4 ++-- cask/src/cask/endpoints/JsonEndpoint.scala | 8 ++++---- cask/src/cask/endpoints/StaticEndpoints.scala | 7 +++---- cask/src/cask/endpoints/WebEndpoints.scala | 3 +-- cask/src/cask/endpoints/WebSocketEndpoint.scala | 4 ++-- cask/src/cask/internal/Conversion.scala | 4 ++-- cask/src/cask/main/Decorators.scala | 10 ++++------ cask/src/cask/main/Main.scala | 3 ++- cask/src/cask/main/Routes.scala | 8 ++++---- cask/src/cask/package.scala | 2 +- docs/pages/1 - Cask: a Scala HTTP micro-framework.md | 14 +++++++------- example/endpoints/app/src/Endpoints.scala | 3 +-- example/formJsonPost/app/src/FormJsonPost.scala | 5 +---- 13 files changed, 34 insertions(+), 41 deletions(-) (limited to 'example') diff --git a/cask/src/cask/endpoints/FormEndpoint.scala b/cask/src/cask/endpoints/FormEndpoint.scala index 436bed4..264c169 100644 --- a/cask/src/cask/endpoints/FormEndpoint.scala +++ b/cask/src/cask/endpoints/FormEndpoint.scala @@ -43,8 +43,8 @@ object FormReader{ def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.map(_.asInstanceOf[FormFile]) } } -class postForm(val path: String, override val subpath: Boolean = false) extends Endpoint { - type InnerReturned = Response.Raw +class postForm(val path: String, override val subpath: Boolean = false) + extends Endpoint[Response.Raw] { val methods = Seq("post") type Input = Seq[FormEntry] diff --git a/cask/src/cask/endpoints/JsonEndpoint.scala b/cask/src/cask/endpoints/JsonEndpoint.scala index 3b3b095..2e3373f 100644 --- a/cask/src/cask/endpoints/JsonEndpoint.scala +++ b/cask/src/cask/endpoints/JsonEndpoint.scala @@ -39,8 +39,8 @@ object JsonData extends DataCompanion[JsonData]{ } } -class postJson(val path: String, override val subpath: Boolean = false) extends Endpoint{ - type InnerReturned = Response[JsonData] +class postJson(val path: String, override val subpath: Boolean = false) + extends Endpoint[Response[JsonData]]{ val methods = Seq("post") type Input = ujson.Value type InputParser[T] = JsReader[T] @@ -79,8 +79,8 @@ class postJson(val path: String, override val subpath: Boolean = false) extends def wrapPathSegment(s: String): Input = ujson.Str(s) } -class getJson(val path: String, override val subpath: Boolean = false) extends Endpoint{ - type InnerReturned = Response[JsonData] +class getJson(val path: String, override val subpath: Boolean = false) + extends Endpoint[Response[JsonData]]{ val methods = Seq("get") type Input = Seq[String] type InputParser[T] = QueryParamReader[T] diff --git a/cask/src/cask/endpoints/StaticEndpoints.scala b/cask/src/cask/endpoints/StaticEndpoints.scala index 401f845..bf99d09 100644 --- a/cask/src/cask/endpoints/StaticEndpoints.scala +++ b/cask/src/cask/endpoints/StaticEndpoints.scala @@ -3,8 +3,7 @@ package cask.endpoints import cask.main.Endpoint import cask.model.Request -class staticFiles(val path: String) extends Endpoint{ - type InnerReturned = String +class staticFiles(val path: String) extends Endpoint[String]{ val methods = Seq("get") type Input = Seq[String] type InputParser[T] = QueryParamReader[T] @@ -22,8 +21,8 @@ class staticFiles(val path: String) extends Endpoint{ def wrapPathSegment(s: String): Input = Seq(s) } -class staticResources(val path: String, resourceRoot: ClassLoader = getClass.getClassLoader) extends Endpoint{ - type InnerReturned = String +class staticResources(val path: String, resourceRoot: ClassLoader = getClass.getClassLoader) + extends Endpoint[String]{ val methods = Seq("get") type Input = Seq[String] type InputParser[T] = QueryParamReader[T] diff --git a/cask/src/cask/endpoints/WebEndpoints.scala b/cask/src/cask/endpoints/WebEndpoints.scala index 7cac4f5..b52b290 100644 --- a/cask/src/cask/endpoints/WebEndpoints.scala +++ b/cask/src/cask/endpoints/WebEndpoints.scala @@ -7,8 +7,7 @@ import cask.model.{Request, Response} import collection.JavaConverters._ -trait WebEndpoint extends Endpoint{ - type InnerReturned = Response.Raw +trait WebEndpoint extends Endpoint[Response.Raw]{ type Input = Seq[String] type InputParser[T] = QueryParamReader[T] def wrapFunction(ctx: Request, diff --git a/cask/src/cask/endpoints/WebSocketEndpoint.scala b/cask/src/cask/endpoints/WebSocketEndpoint.scala index 5f35832..ca2854f 100644 --- a/cask/src/cask/endpoints/WebSocketEndpoint.scala +++ b/cask/src/cask/endpoints/WebSocketEndpoint.scala @@ -13,8 +13,8 @@ object WebsocketResult{ implicit class Listener(val value: WebSocketConnectionCallback) extends WebsocketResult } -class websocket(val path: String, override val subpath: Boolean = false) extends cask.main.BaseEndpoint{ - type InnerReturned = WebsocketResult +class websocket(val path: String, override val subpath: Boolean = false) + extends cask.main.BaseEndpoint[WebsocketResult]{ val methods = Seq("websocket") type Input = Seq[String] type InputParser[T] = QueryParamReader[T] diff --git a/cask/src/cask/internal/Conversion.scala b/cask/src/cask/internal/Conversion.scala index e6a5a47..8d8ee3a 100644 --- a/cask/src/cask/internal/Conversion.scala +++ b/cask/src/cask/internal/Conversion.scala @@ -2,8 +2,8 @@ package cask.internal import scala.annotation.implicitNotFound -@implicitNotFound("Cannot return ${T} as a ${V} response") +@implicitNotFound("Cannot return ${T} as a ${V}") class Conversion[T, V](val f: T => V) object Conversion{ - def create[T, V](implicit f: T => V) = new Conversion(f) + implicit 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 28d44f5..e395d5b 100644 --- a/cask/src/cask/main/Decorators.scala +++ b/cask/src/cask/main/Decorators.scala @@ -9,7 +9,7 @@ import cask.model.{Request, Response} * Annotates a Cask endpoint that returns a HTTP [[Response]]; similar to a * [[Decorator]] but with additional metadata and capabilities. */ -trait Endpoint extends BaseEndpoint { +trait Endpoint[InnerReturned] extends BaseEndpoint[InnerReturned] { type OuterReturned = Router.Result[Response.Raw] } @@ -17,7 +17,7 @@ trait Endpoint extends BaseEndpoint { * An [[Endpoint]] that may return something else than a HTTP response, e.g. * a websocket endpoint which may instead return a websocket event handler */ -trait BaseEndpoint extends BaseDecorator{ +trait BaseEndpoint[InnerReturned] extends BaseDecorator[InnerReturned]{ /** * What is the path that this particular endpoint matches? */ @@ -55,10 +55,9 @@ trait BaseEndpoint extends BaseDecorator{ /** * A [[Decorator]] that may deal with values other than HTTP [[Response]]s */ -trait BaseDecorator{ +trait BaseDecorator[InnerReturned]{ type Input type InputParser[T] <: ArgReader[Input, T, Request] - type InnerReturned type Delegate = Map[String, Input] => Router.Result[InnerReturned] type OuterReturned <: Router.Result[Any] def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned @@ -76,10 +75,9 @@ trait BaseDecorator{ * to `wrapFunction`, which takes a `Map` representing any additional argument * lists (if any). */ -trait Decorator extends BaseDecorator{ +trait Decorator extends BaseDecorator[Response.Raw]{ type OuterReturned = Router.Result[Response.Raw] type Input = Any - type InnerReturned = Response.Raw type InputParser[T] = NoOpParser[Input, T] } diff --git a/cask/src/cask/main/Main.scala b/cask/src/cask/main/Main.scala index 7128006..c3a4ace 100644 --- a/cask/src/cask/main/Main.scala +++ b/cask/src/cask/main/Main.scala @@ -94,7 +94,8 @@ abstract class BaseMain{ case head :: rest => head.wrapFunction( ctx, - args => rec(rest, args :: bindings).asInstanceOf[Router.Result[head.InnerReturned]] + args => rec(rest, args :: bindings) + .asInstanceOf[cask.internal.Router.Result[cask.model.Response.Raw]] ) case Nil => diff --git a/cask/src/cask/main/Routes.scala b/cask/src/cask/main/Routes.scala index a4660a9..5b02542 100644 --- a/cask/src/cask/main/Routes.scala +++ b/cask/src/cask/main/Routes.scala @@ -8,7 +8,7 @@ import language.experimental.macros object Routes{ case class EndpointMetadata[T](decorators: Seq[Decorator], - endpoint: BaseEndpoint, + endpoint: BaseEndpoint[_], entryPoint: EntryPoint[T, _]) case class RoutesEndpointsMetadata[T](value: EndpointMetadata[T]*) object RoutesEndpointsMetadata{ @@ -19,15 +19,15 @@ object Routes{ val routeParts = for{ m <- c.weakTypeOf[T].members - val annotations = m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[BaseDecorator]).reverse + val annotations = m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[BaseDecorator[_]]).reverse if annotations.nonEmpty } yield { - if(!(annotations.head.tree.tpe <:< weakTypeOf[BaseEndpoint])) c.abort( + if(!(annotations.head.tree.tpe <:< weakTypeOf[BaseEndpoint[_]])) c.abort( annotations.head.tree.pos, s"Last annotation applied to a function must be an instance of Endpoint, " + s"not ${annotations.head.tree.tpe}" ) - val allEndpoints = annotations.filter(_.tree.tpe <:< weakTypeOf[BaseEndpoint]) + val allEndpoints = annotations.filter(_.tree.tpe <:< weakTypeOf[BaseEndpoint[_]]) if(allEndpoints.length > 1) c.abort( annotations.head.tree.pos, s"You can only apply one Endpoint annotation to a function, not " + diff --git a/cask/src/cask/package.scala b/cask/src/cask/package.scala index 51aaaf6..c3909ec 100644 --- a/cask/src/cask/package.scala +++ b/cask/src/cask/package.scala @@ -38,6 +38,6 @@ package object cask { val Routes = main.Routes type Main = main.Main type Decorator = main.Decorator - type Endpoint = main.Endpoint + type Endpoint[InnerReturned] = main.Endpoint[InnerReturned] } diff --git a/docs/pages/1 - Cask: a Scala HTTP micro-framework.md b/docs/pages/1 - Cask: a Scala HTTP micro-framework.md index 093c8c3..52653d2 100644 --- a/docs/pages/1 - Cask: a Scala HTTP micro-framework.md +++ b/docs/pages/1 - Cask: a Scala HTTP micro-framework.md @@ -162,13 +162,13 @@ what to do in each case. You can use the `@cask.route` annotation to do so $$$formJsonPost If you need to handle a JSON-encoded POST request, you can use the -`@cask.postJson` decorator. This assumes the posted request body is a JSON dict, -and uses its keys to populate the endpoint's parameters, either as raw -`ujson.Js.Value`s or deserialized into `Seq[Int]`s or other things. -Deserialization is handled using the -[uPickle](https://github.com/lihaoyi/upickle) JSON library, though you could -write your own version of `postJson` to work with any other JSON library of your -choice. +`@cask.postJson` decorator. This assumes the posted request body is a +JSON dict, and uses its keys to populate the endpoint's parameters, +either as raw `ujson.Value`s or deserialized into `Seq[Int]`s or other +things. Deserialization is handled using the +[uPickle](https://github.com/lihaoyi/upickle) JSON library, though you +could write your own version of `postJson` to work with any other JSON +library of your choice. Similarly, you can mark endpoints as `@cask.postForm`, in which case the endpoints params will be taken from the form-encoded POST body either raw (as diff --git a/example/endpoints/app/src/Endpoints.scala b/example/endpoints/app/src/Endpoints.scala index b934769..97da526 100644 --- a/example/endpoints/app/src/Endpoints.scala +++ b/example/endpoints/app/src/Endpoints.scala @@ -1,8 +1,7 @@ package app -class custom(val path: String, val methods: Seq[String]) extends cask.Endpoint{ +class custom(val path: String, val methods: Seq[String]) extends cask.Endpoint[Int]{ type Output = Int - type InnerReturned = Int def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map()).map{num => cask.Response("Echo " + num, statusCode = num) diff --git a/example/formJsonPost/app/src/FormJsonPost.scala b/example/formJsonPost/app/src/FormJsonPost.scala index 6dc82d3..b994ac1 100644 --- a/example/formJsonPost/app/src/FormJsonPost.scala +++ b/example/formJsonPost/app/src/FormJsonPost.scala @@ -1,10 +1,7 @@ package app - -import java.io.ByteArrayInputStream - object FormJsonPost extends cask.MainRoutes{ @cask.postJson("/json") - def jsonEndpoint(value1: ujson.Js.Value, value2: Seq[Int]) = { + def jsonEndpoint(value1: ujson.Value, value2: Seq[Int]) = { "OK " + value1 + " " + value2 } -- cgit v1.2.3 From 0460ad2eca7fcdec1ff29c289dad4ecc76dde9c6 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sat, 14 Sep 2019 18:51:36 +0800 Subject: Make `Input` a type param too --- cask/src/cask/endpoints/FormEndpoint.scala | 5 ++--- cask/src/cask/endpoints/JsonEndpoint.scala | 8 +++----- cask/src/cask/endpoints/StaticEndpoints.scala | 10 ++++------ cask/src/cask/endpoints/WebEndpoints.scala | 3 +-- cask/src/cask/endpoints/WebSocketEndpoint.scala | 5 ++--- cask/src/cask/main/Decorators.scala | 15 +++++++-------- cask/src/cask/main/Main.scala | 2 +- cask/src/cask/main/Routes.scala | 11 +++++------ cask/src/cask/package.scala | 2 +- example/endpoints/app/src/Endpoints.scala | 5 ++--- 10 files changed, 28 insertions(+), 38 deletions(-) (limited to 'example') diff --git a/cask/src/cask/endpoints/FormEndpoint.scala b/cask/src/cask/endpoints/FormEndpoint.scala index 264c169..38ce994 100644 --- a/cask/src/cask/endpoints/FormEndpoint.scala +++ b/cask/src/cask/endpoints/FormEndpoint.scala @@ -44,10 +44,9 @@ object FormReader{ } } class postForm(val path: String, override val subpath: Boolean = false) - extends Endpoint[Response.Raw] { + extends Endpoint[Response.Raw, Seq[FormEntry]] { val methods = Seq("post") - type Input = Seq[FormEntry] type InputParser[T] = FormReader[T] def wrapFunction(ctx: Request, delegate: Delegate): Router.Result[Response.Raw] = { @@ -68,6 +67,6 @@ class postForm(val path: String, override val subpath: Boolean = false) } } - def wrapPathSegment(s: String): Input = Seq(FormValue(s, new io.undertow.util.HeaderMap)) + def wrapPathSegment(s: String): Seq[FormEntry] = Seq(FormValue(s, new io.undertow.util.HeaderMap)) } diff --git a/cask/src/cask/endpoints/JsonEndpoint.scala b/cask/src/cask/endpoints/JsonEndpoint.scala index 2e3373f..c4740fd 100644 --- a/cask/src/cask/endpoints/JsonEndpoint.scala +++ b/cask/src/cask/endpoints/JsonEndpoint.scala @@ -40,9 +40,8 @@ object JsonData extends DataCompanion[JsonData]{ } class postJson(val path: String, override val subpath: Boolean = false) - extends Endpoint[Response[JsonData]]{ + extends Endpoint[Response[JsonData], ujson.Value]{ val methods = Seq("post") - type Input = ujson.Value type InputParser[T] = JsReader[T] override type OuterReturned = Router.Result[Response.Raw] def wrapFunction(ctx: Request, @@ -76,13 +75,12 @@ class postJson(val path: String, override val subpath: Boolean = false) case Right(params) => delegate(params) } } - def wrapPathSegment(s: String): Input = ujson.Str(s) + def wrapPathSegment(s: String): ujson.Value = ujson.Str(s) } class getJson(val path: String, override val subpath: Boolean = false) - extends Endpoint[Response[JsonData]]{ + extends Endpoint[Response[JsonData], Seq[String]]{ val methods = Seq("get") - 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] = { diff --git a/cask/src/cask/endpoints/StaticEndpoints.scala b/cask/src/cask/endpoints/StaticEndpoints.scala index bf99d09..2f0568d 100644 --- a/cask/src/cask/endpoints/StaticEndpoints.scala +++ b/cask/src/cask/endpoints/StaticEndpoints.scala @@ -3,9 +3,8 @@ package cask.endpoints import cask.main.Endpoint import cask.model.Request -class staticFiles(val path: String) extends Endpoint[String]{ +class staticFiles(val path: String) extends Endpoint[String, Seq[String]]{ val methods = Seq("get") - type Input = Seq[String] type InputParser[T] = QueryParamReader[T] override def subpath = true def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned = { @@ -18,13 +17,12 @@ class staticFiles(val path: String) extends Endpoint[String]{ ) } - def wrapPathSegment(s: String): Input = Seq(s) + def wrapPathSegment(s: String): Seq[String] = Seq(s) } class staticResources(val path: String, resourceRoot: ClassLoader = getClass.getClassLoader) - extends Endpoint[String]{ + extends Endpoint[String, Seq[String]]{ val methods = Seq("get") - type Input = Seq[String] type InputParser[T] = QueryParamReader[T] override def subpath = true def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned = { @@ -38,5 +36,5 @@ class staticResources(val path: String, resourceRoot: ClassLoader = getClass.get ) } - def wrapPathSegment(s: String): Input = Seq(s) + def wrapPathSegment(s: String): Seq[String] = Seq(s) } diff --git a/cask/src/cask/endpoints/WebEndpoints.scala b/cask/src/cask/endpoints/WebEndpoints.scala index b52b290..ae1e178 100644 --- a/cask/src/cask/endpoints/WebEndpoints.scala +++ b/cask/src/cask/endpoints/WebEndpoints.scala @@ -7,8 +7,7 @@ import cask.model.{Request, Response} import collection.JavaConverters._ -trait WebEndpoint extends Endpoint[Response.Raw]{ - type Input = Seq[String] +trait WebEndpoint extends Endpoint[Response.Raw, Seq[String]]{ type InputParser[T] = QueryParamReader[T] def wrapFunction(ctx: Request, delegate: Delegate): Router.Result[Response.Raw] = { diff --git a/cask/src/cask/endpoints/WebSocketEndpoint.scala b/cask/src/cask/endpoints/WebSocketEndpoint.scala index ca2854f..dff2232 100644 --- a/cask/src/cask/endpoints/WebSocketEndpoint.scala +++ b/cask/src/cask/endpoints/WebSocketEndpoint.scala @@ -14,14 +14,13 @@ object WebsocketResult{ } class websocket(val path: String, override val subpath: Boolean = false) - extends cask.main.BaseEndpoint[WebsocketResult]{ + extends cask.main.BaseEndpoint[WebsocketResult, Seq[String]]{ val methods = Seq("websocket") - type Input = Seq[String] type InputParser[T] = QueryParamReader[T] type OuterReturned = Router.Result[WebsocketResult] def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned = { delegate(WebEndpoint.buildMapFromQueryParams(ctx)) } - def wrapPathSegment(s: String): Input = Seq(s) + def wrapPathSegment(s: String): Seq[String] = Seq(s) } diff --git a/cask/src/cask/main/Decorators.scala b/cask/src/cask/main/Decorators.scala index e395d5b..dd445a4 100644 --- a/cask/src/cask/main/Decorators.scala +++ b/cask/src/cask/main/Decorators.scala @@ -9,7 +9,7 @@ import cask.model.{Request, Response} * Annotates a Cask endpoint that returns a HTTP [[Response]]; similar to a * [[Decorator]] but with additional metadata and capabilities. */ -trait Endpoint[InnerReturned] extends BaseEndpoint[InnerReturned] { +trait Endpoint[InnerReturned, Input] extends BaseEndpoint[InnerReturned, Input] { type OuterReturned = Router.Result[Response.Raw] } @@ -17,7 +17,7 @@ trait Endpoint[InnerReturned] extends BaseEndpoint[InnerReturned] { * An [[Endpoint]] that may return something else than a HTTP response, e.g. * a websocket endpoint which may instead return a websocket event handler */ -trait BaseEndpoint[InnerReturned] extends BaseDecorator[InnerReturned]{ +trait BaseEndpoint[InnerReturned, Input] extends BaseDecorator[InnerReturned, Input]{ /** * What is the path that this particular endpoint matches? */ @@ -55,10 +55,10 @@ trait BaseEndpoint[InnerReturned] extends BaseDecorator[InnerReturned]{ /** * A [[Decorator]] that may deal with values other than HTTP [[Response]]s */ -trait BaseDecorator[InnerReturned]{ - type Input +trait BaseDecorator[InnerReturned, Input]{ + final type InputTypeAlias = Input type InputParser[T] <: ArgReader[Input, T, Request] - type Delegate = Map[String, Input] => Router.Result[InnerReturned] + final type Delegate = Map[String, Input] => Router.Result[InnerReturned] type OuterReturned <: Router.Result[Any] def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned def getParamParser[T](implicit p: InputParser[T]) = p @@ -75,10 +75,9 @@ trait BaseDecorator[InnerReturned]{ * to `wrapFunction`, which takes a `Map` representing any additional argument * lists (if any). */ -trait Decorator extends BaseDecorator[Response.Raw]{ +trait Decorator extends BaseDecorator[Response.Raw, Any]{ type OuterReturned = Router.Result[Response.Raw] - type Input = Any - type InputParser[T] = NoOpParser[Input, T] + type InputParser[T] = NoOpParser[Any, T] } class NoOpParser[Input, T] extends ArgReader[Input, T, Request] { diff --git a/cask/src/cask/main/Main.scala b/cask/src/cask/main/Main.scala index c3a4ace..1eb2dbc 100644 --- a/cask/src/cask/main/Main.scala +++ b/cask/src/cask/main/Main.scala @@ -99,7 +99,7 @@ abstract class BaseMain{ ) case Nil => - metadata.endpoint.wrapFunction(ctx, endpointBindings => + metadata.endpoint.wrapFunction(ctx, (endpointBindings: Map[String, Any]) => metadata.entryPoint .asInstanceOf[EntryPoint[cask.main.Routes, cask.model.Request]] .invoke( diff --git a/cask/src/cask/main/Routes.scala b/cask/src/cask/main/Routes.scala index 5b02542..ea9d6fe 100644 --- a/cask/src/cask/main/Routes.scala +++ b/cask/src/cask/main/Routes.scala @@ -8,7 +8,7 @@ import language.experimental.macros object Routes{ case class EndpointMetadata[T](decorators: Seq[Decorator], - endpoint: BaseEndpoint[_], + endpoint: BaseEndpoint[_, _], entryPoint: EntryPoint[T, _]) case class RoutesEndpointsMetadata[T](value: EndpointMetadata[T]*) object RoutesEndpointsMetadata{ @@ -19,15 +19,15 @@ object Routes{ val routeParts = for{ m <- c.weakTypeOf[T].members - val annotations = m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[BaseDecorator[_]]).reverse + val annotations = m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[BaseDecorator[_, _]]).reverse if annotations.nonEmpty } yield { - if(!(annotations.head.tree.tpe <:< weakTypeOf[BaseEndpoint[_]])) c.abort( + if(!(annotations.head.tree.tpe <:< weakTypeOf[BaseEndpoint[_, _]])) c.abort( annotations.head.tree.pos, s"Last annotation applied to a function must be an instance of Endpoint, " + s"not ${annotations.head.tree.tpe}" ) - val allEndpoints = annotations.filter(_.tree.tpe <:< weakTypeOf[BaseEndpoint[_]]) + val allEndpoints = annotations.filter(_.tree.tpe <:< weakTypeOf[BaseEndpoint[_, _]]) if(allEndpoints.length > 1) c.abort( annotations.head.tree.pos, s"You can only apply one Endpoint annotation to a function, not " + @@ -45,8 +45,7 @@ object Routes{ q"${annotObjectSyms.head}.convertToResultType", tq"cask.Request", annotObjectSyms.map(annotObjectSym => q"$annotObjectSym.getParamParser"), - annotObjectSyms.map(annotObjectSym => tq"$annotObjectSym.Input") - + annotObjectSyms.map(annotObjectSym => tq"$annotObjectSym.InputTypeAlias") ) val declarations = diff --git a/cask/src/cask/package.scala b/cask/src/cask/package.scala index c3909ec..427f638 100644 --- a/cask/src/cask/package.scala +++ b/cask/src/cask/package.scala @@ -38,6 +38,6 @@ package object cask { val Routes = main.Routes type Main = main.Main type Decorator = main.Decorator - type Endpoint[InnerReturned] = main.Endpoint[InnerReturned] + type Endpoint[InnerReturned, Input] = main.Endpoint[InnerReturned, Input] } diff --git a/example/endpoints/app/src/Endpoints.scala b/example/endpoints/app/src/Endpoints.scala index 97da526..7417289 100644 --- a/example/endpoints/app/src/Endpoints.scala +++ b/example/endpoints/app/src/Endpoints.scala @@ -1,7 +1,7 @@ package app -class custom(val path: String, val methods: Seq[String]) extends cask.Endpoint[Int]{ - type Output = Int +class custom(val path: String, val methods: Seq[String]) + extends cask.Endpoint[Int, Seq[String]]{ def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map()).map{num => cask.Response("Echo " + num, statusCode = num) @@ -10,7 +10,6 @@ class custom(val path: String, val methods: Seq[String]) extends cask.Endpoint[I def wrapPathSegment(s: String) = Seq(s) - type Input = Seq[String] type InputParser[T] = cask.endpoints.QueryParamReader[T] } -- cgit v1.2.3 From edd1f62ce364f8b559431249e2350c2bb75fccec Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sat, 14 Sep 2019 19:22:48 +0800 Subject: Rename `BaseDecorator` -> `Decorator`, `Decorator` -> `RawDecorator`, `BaseEndpoint` -> `Endpoint`, `Endpoint` -> `HttpEndpoint` --- cask/src/cask/decorators/compress.scala | 2 +- cask/src/cask/endpoints/FormEndpoint.scala | 4 +- cask/src/cask/endpoints/JsonEndpoint.scala | 6 +-- cask/src/cask/endpoints/StaticEndpoints.scala | 6 +-- cask/src/cask/endpoints/WebEndpoints.scala | 4 +- cask/src/cask/endpoints/WebSocketEndpoint.scala | 2 +- cask/src/cask/main/Decorators.scala | 60 +++++++++++++------------ cask/src/cask/main/Main.scala | 4 +- cask/src/cask/main/Routes.scala | 12 ++--- cask/src/cask/package.scala | 4 +- cask/test/src/test/cask/FailureTests.scala | 2 +- example/decorated/app/src/Decorated.scala | 4 +- example/decorated2/app/src/Decorated2.scala | 4 +- example/endpoints/app/src/Endpoints.scala | 2 +- 14 files changed, 59 insertions(+), 57 deletions(-) (limited to 'example') diff --git a/cask/src/cask/decorators/compress.scala b/cask/src/cask/decorators/compress.scala index 75e2bed..7a7eefb 100644 --- a/cask/src/cask/decorators/compress.scala +++ b/cask/src/cask/decorators/compress.scala @@ -6,7 +6,7 @@ import cask.internal.Router import cask.model.{Request, Response} import collection.JavaConverters._ -class compress extends cask.Decorator{ +class compress extends cask.RawDecorator{ def wrapFunction(ctx: Request, delegate: Delegate) = { val acceptEncodings = ctx.exchange.getRequestHeaders.get("Accept-Encoding").asScala.flatMap(_.split(", ")) delegate(Map()).map{ v => diff --git a/cask/src/cask/endpoints/FormEndpoint.scala b/cask/src/cask/endpoints/FormEndpoint.scala index 38ce994..6f65786 100644 --- a/cask/src/cask/endpoints/FormEndpoint.scala +++ b/cask/src/cask/endpoints/FormEndpoint.scala @@ -1,7 +1,7 @@ package cask.endpoints import cask.internal.{Router, Util} -import cask.main.Endpoint +import cask.main.HttpEndpoint import cask.model._ import io.undertow.server.handlers.form.FormParserFactory @@ -44,7 +44,7 @@ object FormReader{ } } class postForm(val path: String, override val subpath: Boolean = false) - extends Endpoint[Response.Raw, Seq[FormEntry]] { + extends HttpEndpoint[Response.Raw, Seq[FormEntry]] { val methods = Seq("post") type InputParser[T] = FormReader[T] diff --git a/cask/src/cask/endpoints/JsonEndpoint.scala b/cask/src/cask/endpoints/JsonEndpoint.scala index c4740fd..6d3db82 100644 --- a/cask/src/cask/endpoints/JsonEndpoint.scala +++ b/cask/src/cask/endpoints/JsonEndpoint.scala @@ -3,7 +3,7 @@ package cask.endpoints import java.io.{ByteArrayOutputStream, InputStream, OutputStream, OutputStreamWriter} import cask.internal.{Router, Util} -import cask.main.Endpoint +import cask.main.HttpEndpoint import cask.model.Response.DataCompanion import cask.model.{Request, Response} @@ -40,7 +40,7 @@ object JsonData extends DataCompanion[JsonData]{ } class postJson(val path: String, override val subpath: Boolean = false) - extends Endpoint[Response[JsonData], ujson.Value]{ + extends HttpEndpoint[Response[JsonData], ujson.Value]{ val methods = Seq("post") type InputParser[T] = JsReader[T] override type OuterReturned = Router.Result[Response.Raw] @@ -79,7 +79,7 @@ class postJson(val path: String, override val subpath: Boolean = false) } class getJson(val path: String, override val subpath: Boolean = false) - extends Endpoint[Response[JsonData], Seq[String]]{ + extends HttpEndpoint[Response[JsonData], Seq[String]]{ val methods = Seq("get") type InputParser[T] = QueryParamReader[T] override type OuterReturned = Router.Result[Response.Raw] diff --git a/cask/src/cask/endpoints/StaticEndpoints.scala b/cask/src/cask/endpoints/StaticEndpoints.scala index 2f0568d..0abfcf5 100644 --- a/cask/src/cask/endpoints/StaticEndpoints.scala +++ b/cask/src/cask/endpoints/StaticEndpoints.scala @@ -1,9 +1,9 @@ package cask.endpoints -import cask.main.Endpoint +import cask.main.HttpEndpoint import cask.model.Request -class staticFiles(val path: String) extends Endpoint[String, Seq[String]]{ +class staticFiles(val path: String) extends HttpEndpoint[String, Seq[String]]{ val methods = Seq("get") type InputParser[T] = QueryParamReader[T] override def subpath = true @@ -21,7 +21,7 @@ class staticFiles(val path: String) extends Endpoint[String, Seq[String]]{ } class staticResources(val path: String, resourceRoot: ClassLoader = getClass.getClassLoader) - extends Endpoint[String, Seq[String]]{ + extends HttpEndpoint[String, Seq[String]]{ val methods = Seq("get") type InputParser[T] = QueryParamReader[T] override def subpath = true diff --git a/cask/src/cask/endpoints/WebEndpoints.scala b/cask/src/cask/endpoints/WebEndpoints.scala index ae1e178..8bb3bae 100644 --- a/cask/src/cask/endpoints/WebEndpoints.scala +++ b/cask/src/cask/endpoints/WebEndpoints.scala @@ -1,13 +1,13 @@ package cask.endpoints import cask.internal.Router -import cask.main.Endpoint +import cask.main.HttpEndpoint import cask.model.{Request, Response} import collection.JavaConverters._ -trait WebEndpoint extends Endpoint[Response.Raw, Seq[String]]{ +trait WebEndpoint extends HttpEndpoint[Response.Raw, Seq[String]]{ type InputParser[T] = QueryParamReader[T] def wrapFunction(ctx: Request, delegate: Delegate): Router.Result[Response.Raw] = { diff --git a/cask/src/cask/endpoints/WebSocketEndpoint.scala b/cask/src/cask/endpoints/WebSocketEndpoint.scala index dff2232..842d508 100644 --- a/cask/src/cask/endpoints/WebSocketEndpoint.scala +++ b/cask/src/cask/endpoints/WebSocketEndpoint.scala @@ -14,7 +14,7 @@ object WebsocketResult{ } class websocket(val path: String, override val subpath: Boolean = false) - extends cask.main.BaseEndpoint[WebsocketResult, Seq[String]]{ + extends cask.main.Endpoint[WebsocketResult, Seq[String]]{ val methods = Seq("websocket") type InputParser[T] = QueryParamReader[T] type OuterReturned = Router.Result[WebsocketResult] diff --git a/cask/src/cask/main/Decorators.scala b/cask/src/cask/main/Decorators.scala index dd445a4..573c139 100644 --- a/cask/src/cask/main/Decorators.scala +++ b/cask/src/cask/main/Decorators.scala @@ -4,20 +4,41 @@ import cask.internal.{Conversion, Router} import cask.internal.Router.ArgReader import cask.model.{Request, Response} +/** + * A [[Decorator]] allows you to annotate a function to wrap it, via + * `wrapFunction`. You can use this to perform additional validation before or + * after the function runs, provide an additional parameter list of params, + * open/commit/rollback database transactions before/after the function runs, + * or even retrying the wrapped function if it fails. + * + * Calls to the wrapped function are done on the `delegate` parameter passed + * to `wrapFunction`, which takes a `Map` representing any additional argument + * lists (if any). + */ +trait Decorator[InnerReturned, Input]{ + final type InputTypeAlias = Input + type InputParser[T] <: ArgReader[Input, T, Request] + final type Delegate = Map[String, Input] => Router.Result[InnerReturned] + type OuterReturned <: Router.Result[Any] + def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned + def getParamParser[T](implicit p: InputParser[T]) = p +} /** - * Annotates a Cask endpoint that returns a HTTP [[Response]]; similar to a - * [[Decorator]] but with additional metadata and capabilities. + * A [[RawDecorator]] is a decorator that operates on the raw request and + * response stream, before and after the primary [[Endpoint]] does it's job. */ -trait Endpoint[InnerReturned, Input] extends BaseEndpoint[InnerReturned, Input] { +trait RawDecorator extends Decorator[Response.Raw, Any]{ type OuterReturned = Router.Result[Response.Raw] + type InputParser[T] = NoOpParser[Any, T] } + /** - * An [[Endpoint]] that may return something else than a HTTP response, e.g. + * An [[HttpEndpoint]] that may return something else than a HTTP response, e.g. * a websocket endpoint which may instead return a websocket event handler */ -trait BaseEndpoint[InnerReturned, Input] extends BaseDecorator[InnerReturned, Input]{ +trait Endpoint[InnerReturned, Input] extends Decorator[InnerReturned, Input]{ /** * What is the path that this particular endpoint matches? */ @@ -42,7 +63,7 @@ trait BaseEndpoint[InnerReturned, Input] extends BaseDecorator[InnerReturned, In } /** - * [[Endpoint]]s are unique among decorators in that they alone can bind + * [[HttpEndpoint]]s are unique among decorators in that they alone can bind * path segments to parameters, e.g. binding `/hello/:world` to `(world: Int)`. * In order to do so, we need to box up the path segment strings into an * [[Input]] so they can later be parsed by [[getParamParser]] into an @@ -53,33 +74,14 @@ trait BaseEndpoint[InnerReturned, Input] extends BaseDecorator[InnerReturned, In } /** - * A [[Decorator]] that may deal with values other than HTTP [[Response]]s - */ -trait BaseDecorator[InnerReturned, Input]{ - final type InputTypeAlias = Input - type InputParser[T] <: ArgReader[Input, T, Request] - final type Delegate = Map[String, Input] => Router.Result[InnerReturned] - type OuterReturned <: Router.Result[Any] - def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned - def getParamParser[T](implicit p: InputParser[T]) = p -} - -/** - * A decorator allows you to annotate a function to wrap it, via - * `wrapFunction`. You can use this to perform additional validation before or - * after the function runs, provide an additional parameter list of params, - * open/commit/rollback database transactions before/after the function runs, - * or even retrying the wrapped function if it fails. - * - * Calls to the wrapped function are done on the `delegate` parameter passed - * to `wrapFunction`, which takes a `Map` representing any additional argument - * lists (if any). + * Annotates a Cask endpoint that returns a HTTP [[Response]]; similar to a + * [[RawDecorator]] but with additional metadata and capabilities. */ -trait Decorator extends BaseDecorator[Response.Raw, Any]{ +trait HttpEndpoint[InnerReturned, Input] extends Endpoint[InnerReturned, Input] { type OuterReturned = Router.Result[Response.Raw] - type InputParser[T] = NoOpParser[Any, T] } + class NoOpParser[Input, T] extends ArgReader[Input, T, Request] { def arity = 1 diff --git a/cask/src/cask/main/Main.scala b/cask/src/cask/main/Main.scala index 1eb2dbc..32e6517 100644 --- a/cask/src/cask/main/Main.scala +++ b/cask/src/cask/main/Main.scala @@ -28,7 +28,7 @@ class Main(servers0: Routes*) extends BaseMain{ def allRoutes = servers0.toSeq } abstract class BaseMain{ - def mainDecorators = Seq.empty[cask.main.Decorator] + def mainDecorators = Seq.empty[cask.main.RawDecorator] def allRoutes: Seq[Routes] def port: Int = 8080 def host: String = "localhost" @@ -88,7 +88,7 @@ abstract class BaseMain{ case None => writeResponse(exchange, handleNotFound()) case Some(((routes, metadata), routeBindings, remaining)) => val ctx = Request(exchange, remaining) - def rec(remaining: List[Decorator], + def rec(remaining: List[RawDecorator], bindings: List[Map[String, Any]]): Router.Result[Any] = try { remaining match { case head :: rest => diff --git a/cask/src/cask/main/Routes.scala b/cask/src/cask/main/Routes.scala index ea9d6fe..f133fdb 100644 --- a/cask/src/cask/main/Routes.scala +++ b/cask/src/cask/main/Routes.scala @@ -7,8 +7,8 @@ import scala.reflect.macros.blackbox.Context import language.experimental.macros object Routes{ - case class EndpointMetadata[T](decorators: Seq[Decorator], - endpoint: BaseEndpoint[_, _], + case class EndpointMetadata[T](decorators: Seq[RawDecorator], + endpoint: Endpoint[_, _], entryPoint: EntryPoint[T, _]) case class RoutesEndpointsMetadata[T](value: EndpointMetadata[T]*) object RoutesEndpointsMetadata{ @@ -19,15 +19,15 @@ object Routes{ val routeParts = for{ m <- c.weakTypeOf[T].members - val annotations = m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[BaseDecorator[_, _]]).reverse + val annotations = m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[Decorator[_, _]]).reverse if annotations.nonEmpty } yield { - if(!(annotations.head.tree.tpe <:< weakTypeOf[BaseEndpoint[_, _]])) c.abort( + if(!(annotations.head.tree.tpe <:< weakTypeOf[Endpoint[_, _]])) c.abort( annotations.head.tree.pos, s"Last annotation applied to a function must be an instance of Endpoint, " + s"not ${annotations.head.tree.tpe}" ) - val allEndpoints = annotations.filter(_.tree.tpe <:< weakTypeOf[BaseEndpoint[_, _]]) + val allEndpoints = annotations.filter(_.tree.tpe <:< weakTypeOf[Endpoint[_, _]]) if(allEndpoints.length > 1) c.abort( annotations.head.tree.pos, s"You can only apply one Endpoint annotation to a function, not " + @@ -70,7 +70,7 @@ object Routes{ trait Routes{ - def decorators = Seq.empty[cask.main.Decorator] + def decorators = Seq.empty[cask.main.RawDecorator] private[this] var metadata0: Routes.RoutesEndpointsMetadata[this.type] = null def caskMetadata = if (metadata0 != null) metadata0 diff --git a/cask/src/cask/package.scala b/cask/src/cask/package.scala index 427f638..d17cfe6 100644 --- a/cask/src/cask/package.scala +++ b/cask/src/cask/package.scala @@ -37,7 +37,7 @@ package object cask { type Routes = main.Routes val Routes = main.Routes type Main = main.Main - type Decorator = main.Decorator - type Endpoint[InnerReturned, Input] = main.Endpoint[InnerReturned, Input] + type RawDecorator = main.RawDecorator + type HttpEndpoint[InnerReturned, Input] = main.HttpEndpoint[InnerReturned, Input] } diff --git a/cask/test/src/test/cask/FailureTests.scala b/cask/test/src/test/cask/FailureTests.scala index ac0f3d8..6b01ec1 100644 --- a/cask/test/src/test/cask/FailureTests.scala +++ b/cask/test/src/test/cask/FailureTests.scala @@ -4,7 +4,7 @@ import cask.model.Request import utest._ object FailureTests extends TestSuite { - class myDecorator extends cask.Decorator { + class myDecorator extends cask.RawDecorator { def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned = { delegate(Map("extra" -> 31337)) } diff --git a/example/decorated/app/src/Decorated.scala b/example/decorated/app/src/Decorated.scala index b3f1149..a88bd2e 100644 --- a/example/decorated/app/src/Decorated.scala +++ b/example/decorated/app/src/Decorated.scala @@ -3,12 +3,12 @@ object Decorated extends cask.MainRoutes{ class User{ override def toString = "[haoyi]" } - class loggedIn extends cask.Decorator { + class loggedIn extends cask.RawDecorator { def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map("user" -> new User())) } } - class withExtra extends cask.Decorator { + class withExtra extends cask.RawDecorator { def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map("extra" -> 31337)) } diff --git a/example/decorated2/app/src/Decorated2.scala b/example/decorated2/app/src/Decorated2.scala index fa4b1c5..305281a 100644 --- a/example/decorated2/app/src/Decorated2.scala +++ b/example/decorated2/app/src/Decorated2.scala @@ -3,12 +3,12 @@ object Decorated2 extends cask.MainRoutes{ class User{ override def toString = "[haoyi]" } - class loggedIn extends cask.Decorator { + class loggedIn extends cask.RawDecorator { def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map("user" -> new User())) } } - class withExtra extends cask.Decorator { + class withExtra extends cask.RawDecorator { def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map("extra" -> 31337)) } diff --git a/example/endpoints/app/src/Endpoints.scala b/example/endpoints/app/src/Endpoints.scala index 7417289..9722673 100644 --- a/example/endpoints/app/src/Endpoints.scala +++ b/example/endpoints/app/src/Endpoints.scala @@ -1,7 +1,7 @@ package app class custom(val path: String, val methods: Seq[String]) - extends cask.Endpoint[Int, Seq[String]]{ + extends cask.HttpEndpoint[Int, Seq[String]]{ def wrapFunction(ctx: cask.Request, delegate: Delegate): OuterReturned = { delegate(Map()).map{num => cask.Response("Echo " + num, statusCode = num) -- cgit v1.2.3