Are imports and conditionals in Play's routes file possible? - scala

I know that the earlier versions of Play used to support routes and conditionals (if blocks) in the routes file but I cannot find any such documentation for Play 2.2.x and HTTP routing says nothing about such a feature.
I want to replace this:
GET /api/users/:id com.corporate.project.controllers.UserController.get(id)
with a shorter version using import as follows:
import com.corporate.project.controllers._
GET /api/users/:id UserController.get(id)
Also, is it possible to have conditionals in the routes file? e.g.
if Play.isDev(Play.current())
GET /displayConfig DebugController.displayServerConfigs()

Package imports used to be possible with an SBT setting: routesImport += "com.corporate.project.controllers._". Not sure if it is still the case.
Also, is it possible to have conditionals in the routes file?
It might not be an ideal solution but we use routes tags to deal with this kind of conditional routes. You need a filter that checks if the route is annotated and runs your conditional logic.
Routes:
# #devmode
GET /displayConfig DebugController.displayServerConfigs()
Filter:
object DevmodeRouteFilter extends Filter {
private val DevmodeAnnotation = "#devmode"
override def apply(next: RequestHeader => Future[SimpleResult])(request: RequestHeader): Future[SimpleResult] = {
if(isDevRoute(request) && !Play.isDev(Play.current()) {
Play.current.global.onHandlerNotFound(request)
} else {
next(request)
}
}
private def isDevRoute(request: RequestHeader): Boolean = {
val comments = request.tags.getOrElse(Routes.ROUTE_COMMENTS, "")
comments.lines.exists { comment =>
comment.trim == DevmodeAnnotation
}
}
}
Don't forget to add the filter to the filter chain.

Thank you for your question, it is an interesting idea, so I went to the sources of Play2 and found that in my checkout of Play 2.2.3 in the play.core.Router.scala: object HandlerInvoker constructs controller as
handlerDef.ref.getClass.getClassLoader.loadClass(handlerDef.controller)
So I am afraid that support for partial class names (stored in handlerDef.controller) is not implemented in 2.2.x.
In 2.3.x there is commit https://github.com/playframework/playframework/commit/ba98dabd7ba374ed98f14f721835b898d27dd55b
that brought notion of package/namespace for the routes.
Please see changes in Routes.scala and RoutesCompiler.scala from that commit.
But I can't find description of that functionality in the documentation and I do not have Play 2.3.x to play with it now.
P.S.: Sample magic for advanced routing for 2.3.x described in http://java.dzone.com/articles/advanced-routing-play .

Related

How to successfully change `FakeRequest` to `RequestBuilder`

As the Play 2.4 migration guide specifies:
FakeRequest has been replaced by RequestBuilder.
I have a question how to change or migrate play.api.test.FakeRequest to Http.RequestBuilder when I have something like:
val result = controller.update.apply {
FakeRequest()
.withLoggedIn(config)(loggedInUser)
}
So how to use withLoggedIn in RequestBuilder. I don't see anything similar.

How to get ROUTE_PATTERN from request in play 2.6 scala

I've extracted ROUTE_PATTERN in play 2.5 with:
request.tags.get("ROUTE_PATTERN")
now I updated play 2.6 and this code doesn't work anymore. I read play documentation here:
What’s new in Play 2.6
I tried:
object Attrs {
val RoutePattern: TypedKey[String] = TypedKey("ROUTE_PATTERN")
}
request.attrs.get(Attrs.RoutePattern)
It always returns None. How I can get the RoutePattern of request in play 2.6?
From the 2.6 migration guide:
If you used any of the Router.Tags.* tags, you should change your code to use the new Router.Attrs.HandlerDef (Scala)....
This new attribute contains a HandlerDef object with all the information that is currently in the tags. The current tags all correspond to a field in the HandlerDef object....
The field in HandlerDef that corresponds to the old ROUTE_PATTERN tag is path:
import play.api.routing.{ HandlerDef, Router }
import play.api.mvc.RequestHeader
val handler = request.attrs(Router.Attrs.HandlerDef)
val routePattern = handler.path

Scala - play2 config get section

I decided to try scala out with play2. I am trying to somehow get a config section out of application config. It looks like this (by section I mean whole mail part)
services: {
rest: {
mail: {
uri: "xyz",
authorization: {
username: "xyz",
password: "xyz"
}
}
}
}
Code
import com.typesafe.config.ConfigObject
import play.api.Play.current
val config: Option[ConfigObject] = current.configuration.getObject("services.rest.mail")
This gives Some(SimpleConfigObject()) and trough there the only way I am able to actually get mail section and use it as a ConfigObject is trough
config.get.toConfig.getString("uri")
Or I can get the actual value with
config.get.get("uri").unwrapped().toString
Or for fun:
config.get.toConfig.getObject("authorization").toConfig.getString("username")
Either way it seems to me I am doing it overly complicated. Is there some easier way to get a section from config?
Since the config library has a Java API, it can feel a bit verbose using it from Scala. There are some Scala wrappers though that enable more compact syntax. See https://github.com/typesafehub/config#scala-wrappers-for-the-java-library.
I will post this as an answer for future reference.
After another while of playing with the code I have found parseResourcesAnySyntax method which does exactly what I want and since I have my config split into multiple parts for separate environments (application.dev.conf, etc.) I can simply do
import com.typesafe.config.{Config, ConfigFactory}
import play.api.Play._
val config: Config = ConfigFactory.parseResourcesAnySyntax("application.%s.conf" format current.mode)
and then use
config.getString("uri")
// or
config.getString("authorization.username")
// or if I want to use part of it as another section
val section: Config = config.getConfig("authorization")
section.getString("username")
Of course, another viable alternative is using a wrapper as mister Stebel recommended.

How can I serve mostly static pages from a scala Play! application like an "About" page in different languages?

I am building an application using the Play! framework version 2.0.4. Some pages that are legally required such as an imprint mostly contain lots (some 10k each) of static text which I would like to provide in different languages.
In versions 1.x there was an #include directive that allowed to construct the actual resource path using the current Lang.
What is the recommended way of implementing sth similar with Play 2.x?
Thank you & best regards,
Erich
I'm not 100% certain how you implement it as of now, but here's what I've come up with.
You could just write your own include helper. Save the following in a file Helpers.scala in your views folder. Explanations are in the code's comments.
package views.html.helper
object include {
import play.api.templates.Html
import play.api.Play
import play.api.Play.current
import play.api.i18n._
// The default is to look in the public dir, but you can change it if necessary
def apply(filePath: String, rootDir: String = "public")(implicit lang: Lang): Html = {
// Split filePath at name and suffix to insert the language in between them
val (fileName, suffix) = filePath.splitAt(filePath.lastIndexOf("."))
// Retrieve the file with the current language, or as a fallback, without any suffix
val maybeFile =
Play.getExistingFile(rootDir + "/" + fileName + "_" + lang.language + suffix).
orElse(Play.getExistingFile(rootDir + "/" + filePath))
// Read the file's content and wrap it in HTML or return an error message
maybeFile map { file =>
val content = scala.io.Source.fromFile(file).mkString
Html(content)
} getOrElse Html("File Not Found")
}
}
Now in your imprint.scala.html you could call it like this:
#()(implicit lang: Lang)
#import helper._
#include("static/imprint.html")
Way showed by Schleichardt was used in play-authenticate to select mail templates in different languages and now it's changed to work with reflections on the controller, so maybe it can be interesting for you. Anyway it was intendent to keep standard templating possibilities (cause each mail need to be personalized before send)
For static info pages, you can just save the code for each language with suffix ie. impressum_en.html, impressum_de.html in the file system and use simple controller which will find file with proper suffix and return its content exactly as it is... all you need probably to return Ok(fileContent) and set the Content-Type manually to text/html.
Other option is doing the similar thing but storing it in the DB, so you can create simple back-end and edit it with browser.
If you still need to replace some elements, you can do it with some ###MARKER### in the code + simple String operations, or with JavaScript on the client side.
Templates in 2.0 work a bit differently. The Scala based templates are compiled. From one template instead of 'including' another, you can make a call to it. I'm not sure exactly what you mean by the language thing. In 2.0 though, a parameter to the template could be the language.
Example template named 'included' in package whateverpackage.
#(lang: Lang)
...
Example calling a template named 'included':
#import whateverpackage._
#included(Lang("en"))
#()(implicit lang: Lang)
#main(Messages("imprint")) {
#lang match {
case Lang("de", _) => { #germanImprint() #* uses app/views/germanImprint.scala.html file *#}
case _ => { #englishImprint() }
}
}

MVC on Lift/Scala

Has anyone tried to do a scala/lift application using MVC instead of view-first?
I know that you can create Controllers/views as:
package test.test.test.view
...
Lots of imports
...
class MvcRocks extends LiftView {
def dispatch = {
case "rule" => ruleDispatch _
case "bar" => barDispatch _
}
def barDispatch(): Box[NodeSeq] = {
Full(<lift:embed what="/mvc_rucks/bar" />)
}
}
And this code will be accessible if you add it to the menu(in the boot), even if its hidden as:
val entries = Menu(Loc("Home", List("index"), "Home")) ::
List(Menu(Loc("MvcRock", List("mvc_rocks", "bar"), "Mvc really Rocks", Hidden)))
LiftRules.setSiteMap(SiteMap(entries:_*))
Now, of course this will make it so, you declare every action in the menu, then have a case for each action(per controller) and that would open the "view" (that would be a file in /mvc_rucks/bar.html).
My question is, if you would implement a full mvc, you would need to put all your logic in the action barDispatch, but how would you send those variables to the HTML template? and how would you receive post/get information?
(Notice that if you html code has lift bindings, it will of course act as view-first, even after you did MVC before).
Since your question is not specific to Lift, I'd recommend you the Playframework. The version 1.1 supports Scala 2.8.
Playframework is totally MVC with a fantastic template engine and allows you to choose freely between java/scala.
And I say: To use Play, you don't need 'nuclear scientist knowledge'. Try it!