def any(routeMatchers: RouteMatcher*)(action: => Any) = {
get(routeMatchers : _*)(action)
post(routeMatchers : _*)(action)
put(routeMatchers : _*)(action)
delete(routeMatchers : _*)(action)
}
--
Ross A. Baker
ba...@alumni.indiana.edu
Indianapolis, IN, USA
What exactly do want to support here? Simple prefix matching would be easy:
// Calls UserApp, strips /user off the path for prefix matching.
mount("/user", new UserRoutes)
To harness the full power of route matching is more complicated:
get("/user/*", Moon.phase == Full) { new UserRoutes with WerewolfSupport }
How exactly do we know to strip "/user" when calling UserRoutes? We
don't currently have a way to detect the "unconsumed" part of a URL,
nor even a definition of what the unconsumed part is. What do the
inner routes see if the outer matcher is "/user/*.jpg"? We'd need
some sort of (Request=>Request) decorator for the inner routes, and
detailed rules on how route matchers build up that decorator. More
powerful, but far more complicated. Worth it? I'm not sure.
Other alternatives to the multi-servlet workaround:
1) Create each set of routes as a trait, compose them: class MyApp
extends ScalatraServlet with UserRoutes with MailRoutes.. (Downside:
paths are still absolute, but reusable if they don't change from app
to app.)
2) Like above, but build calculate paths on the fly with an abstract
member. (Downside: totally untested. I just thought of it.)
trait UserRoutes { this: => ScalatraKernel
def userPrefix: String = "/users"
get(userPrefix + "/:id") { ... }
delete(userPrefix + "/:id") { ... }
}
class MyApp extends ScalatraServlet with UserRoutes {
// This is going to be a German app
val userPrefix = "/benutzer"
}
--
> Mountable routers are definitely on our radar. Sinatra does not
> provide us with a model in this case, but some of the other mentioned
> frameworks do.
*Devils' Advocate*
I used to use Restlets and it allows this kind of complexity, not to mention adding filters to various routers, etc, etc. It's a great framework and it taught me a lot about the concerns of the REST-ful style that I might otherwise have glossed over under time constraints.
The best thing about Scalatra, though, is that it's simple. It rewards "keeping it simple", encourages refactoring your design to fit within super-simple constraints, and the fact that it doesn't allow too many complicated compositions forces (or at least allows) you to realize that you're over-engineering. Scalatra resists over-abstraction, I think. Keeps the actual code reasonably simple, too.
I think having multiple servlets, each one a context, is perfectly fine and already adequate and lifts the concern out of Scalatra itself. (I use this pattern to serve static content with Jetty's resource handler, and my API via a Scalatra servlet.)
If you have lots of support methods you want to use across multiple servlets (/app/admin and /app/public, etc, etc), then a trait can help with that. The interesting side effect is that if you need to move a servlet (a collection of routes) from one app to another (for instance, to separate routes that ingest a lot of data from those that serve it for performance reasons), it's much easier to cut/copy/paste a nice little servlet class out of one project and inject it into another.
The composability should be at the container layer, not Scalatra itself, which should never encourage giant, monolithic apps.
Keith
Good post. Sinatra has spread to most languages capable of imitating
its DSL because of its simplicity. It's sort of the universal web
framework, and we do well not to stray far from it.
Part of what makes the multiple servlet solution so distasteful is the
web.xml. As we decouple the routing from the servlet, it will be
possible to configure multiple applications in pure Scala code. I
prefer toward standard .war deployments, but if you start your own
embedded Jetty instance and construct the servlets yourself, you can
even configure in pure code today.
http://github.com/scalatra/scalatra-netty
Thanks
Netty is an alternative to servlets. scalatra-netty will not
currently run on a servlet container, but a servlet implementation
will be supported if and when that project gets merged into master.
--