diff options
-rw-r--r-- | cask/src/cask/internal/Router.scala | 25 | ||||
-rw-r--r-- | cask/src/cask/main/Routes.scala | 11 | ||||
-rw-r--r-- | cask/test/src/test/cask/FailureTests.scala | 53 |
3 files changed, 79 insertions, 10 deletions
diff --git a/cask/src/cask/internal/Router.scala b/cask/src/cask/internal/Router.scala index 460c5e4..98a10d1 100644 --- a/cask/src/cask/internal/Router.scala +++ b/cask/src/cask/internal/Router.scala @@ -187,7 +187,7 @@ class Router[C <: Context](val c: C) { } } - def extractMethod(meth: MethodSymbol, + def extractMethod(method: MethodSymbol, curCls: c.universe.Type, wrapOutput: c.Tree => c.Tree, ctx: c.Type, @@ -205,14 +205,19 @@ class Router[C <: Context](val c: C) { } yield l.value.value.asInstanceOf[String] (remaining, docValues.headOption) } - val (_, methodDoc) = getDocAnnotation(meth.annotations) + val (_, methodDoc) = getDocAnnotation(method.annotations) val argListSymbol = q"${c.fresh[TermName]("argsList")}" - val argData = for(argListIndex <- 0 until meth.paramLists.length) yield{ + if (method.paramLists.length != argReaders.length) c.abort( + method.pos, + s"Endpoint ${method.name}'s number of parameter lists (${method.paramLists.length}) " + + s"doesn't match number of decorators (${argReaders.length})" + ) + val argData = for(argListIndex <- 0 until method.paramLists.length) yield{ val annotDeserializeType = annotDeserializeTypes(argListIndex) val argReader = argReaders(argListIndex) - val flattenedArgLists = meth.paramss(argListIndex) + val flattenedArgLists = method.paramss(argListIndex) def hasDefault(i: Int) = { - val defaultName = s"${meth.name}$$default$$${i + 1}" + val defaultName = s"${method.name}$$default$$${i + 1}" if (curCls.members.exists(_.name.toString == defaultName)) Some(defaultName) else None } @@ -275,7 +280,7 @@ class Router[C <: Context](val c: C) { """ val reader = - if (vararg) c.abort(meth.pos, "Varargs are not supported in cask routes") + if (vararg) c.abort(method.pos, "Varargs are not supported in cask routes") else q""" cask.internal.Router.makeReadCall( @@ -285,7 +290,7 @@ class Router[C <: Context](val c: C) { $argSig.asInstanceOf[cask.internal.Router.ArgSig[Any, _, _, cask.model.ParamContext]] ) """ - c.internal.setPos(reader, meth.pos) + c.internal.setPos(reader, method.pos) (reader, argSig) } @@ -307,12 +312,12 @@ class Router[C <: Context](val c: C) { val argSigs = argData.map(_._2) val argNames = argData.map(_._3) val readArgs = argData.map(_._4) - var methodCall: c.Tree = q"$baseArgSym.${meth.name.toTermName}" + var methodCall: c.Tree = q"$baseArgSym.${method.name.toTermName}" for(argNameCast <- argNameCasts) methodCall = q"$methodCall(..$argNameCast)" val res = q""" cask.internal.Router.EntryPoint[$curCls, $ctx]( - ${meth.name.toString}, + ${method.name.toString}, ${argSigs.toList}, ${methodDoc match{ case None => q"scala.None" @@ -330,7 +335,7 @@ class Router[C <: Context](val c: C) { """ c.internal.transform(res){(t, a) => - c.internal.setPos(t, meth.pos) + c.internal.setPos(t, method.pos) a.default(t) } diff --git a/cask/src/cask/main/Routes.scala b/cask/src/cask/main/Routes.scala index 6f5b454..c3d2bfc 100644 --- a/cask/src/cask/main/Routes.scala +++ b/cask/src/cask/main/Routes.scala @@ -58,6 +58,17 @@ object Routes{ val annotations = m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[BaseDecorator]).reverse if annotations.nonEmpty } yield { + 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[Endpoint[_]]) + if(allEndpoints.length > 1) c.abort( + annotations.head.tree.pos, + s"You can only apply one Endpoint annotation to a function, not " + + s"${allEndpoints.length} in ${allEndpoints.map(_.tree.tpe).mkString(", ")}" + ) val annotObjects = for(annot <- annotations) yield q"new ${annot.tree.tpe}(..${annot.tree.children.tail})" diff --git a/cask/test/src/test/cask/FailureTests.scala b/cask/test/src/test/cask/FailureTests.scala new file mode 100644 index 0000000..fcfed79 --- /dev/null +++ b/cask/test/src/test/cask/FailureTests.scala @@ -0,0 +1,53 @@ +package test.cask + +import cask.model.ParamContext +import utest._ + +object FailureTests extends TestSuite { + class myDecorator extends cask.Routes.Decorator { + def getParamValues(ctx: ParamContext) = Map("extra" -> 31337) + } + val tests = Tests{ + + 'mismatchedDecorators - { + utest.compileError(""" + object Decorated extends cask.MainRoutes{ + @cask.get("/hello/:world") + def hello(world: String)(extra: Int) = world + extra + initialize() + } + """).msg ==> + "Endpoint hello's number of parameter lists (2) doesn't match number of decorators (1)" + + utest.compileError(""" + object Decorated extends cask.MainRoutes{ + @myDecorator() + @cask.get("/hello/:world") + def hello(world: String)= world + initialize() + } + """).msg ==> + "Endpoint hello's number of parameter lists (1) doesn't match number of decorators (2)" + + utest.compileError(""" + object Decorated extends cask.MainRoutes{ + @cask.get("/hello/:world") + @myDecorator() + def hello(world: String)(extra: Int)= world + initialize() + } + """).msg ==> + "Last annotation applied to a function must be an instance of Endpoint, not test.cask.FailureTests.myDecorator" + + utest.compileError(""" + object Decorated extends cask.MainRoutes{ + @cask.get("/hello/:world") + @cask.get("/hello/:world") + def hello(world: String)(extra: Int)= world + initialize() + } + """).msg ==> + "You can only apply one Endpoint annotation to a function, not 2 in cask.endpoints.get, cask.endpoints.get" + } + } +} |