Question

Having trouble accessing the compiled assets location in production.

My strategy has been to serve my assets in "app/assets/ui" when in development and "public" when in production this is done as shown below in my conf/routes file

#{if(play.Play.mode.isDev())}
  GET     /assets/*file    controllers.common.Assets.at(path="/app/assets/ui", file)
#{/}
#{else}
  GET     /assets/*file controllers.common.Assets.at(path="/public", file)
#{/}

Since i have defined asset mappings outside “public,”I have added the following line in my Build.scala

playAssetsDirectories <+= baseDirectory / "app/assets/ui"

As an example my scripts are loaded conditionaly depending on the environment as shown below

@if(play.Play.isDev()) {<script src="@routes.Assets.at("/app/assets/ui", "javascripts/application.js")"type="text/javascript"></script>} else {<script src="@.routes.Assets.at("/public", "javascripts/application.min.js")" type="text/javascript"></script>}

I'm using Grunt for my frontend workflow and when the application builds it copies the distribution files to the application's public folder. I start the app in production using "sbt clean compile stage" and then run the packaged app.

My problem appears that the routes are still referring to the "app/assets/ui" folder instead of the distribution "public" folder.

Any tips on how i can debug this? My working background is as a front end developer so i'm very new to Play! and scala.

Was it helpful?

Solution

As mentioned by @estmatic, your conditional in routes won't be evaluated.

As it's generally extremely useful to consolidate the differences between application Modes into files, I'd suggest you extend GlobalSettings (if you aren't already) and override the onLoadConfig method:

class Settings extends GlobalSettings {

  override def onLoadConfig(config: Configuration, path: File, classloader: ClassLoader, mode: Mode.Mode): Configuration = {

    val specificConfig:Config =  // ... Use the mode param to load appropriate file        

    super.onLoadConfig(specificConfig, path, classloader, mode)
  }
  ...
}

You could then have appropriately-named files (dev.conf and production.conf spring to mind) that contain suitable values, one of them being the base path for the Assets controller to use.

EDIT turns out doing it this way makes usage in routes awkward, here's another approach:

This approach does not use a configuration file per-environment, which means that if something changes in the frontend configuration (e.g. it's no longer served up from /public) you'll have to change this code and re-deploy it. However, it fits into Play 2.x pretty nicely:

package controllers

object EnvironmentSpecificAssets extends AssetsBuilder {

  val modeToAssetsPathMap = Map(
    Mode.Dev -> "/app/assets/ui",
    Mode.Prod -> "/public")

  lazy val modePath = modeToAssetsPathMap(Play.current.mode)

  /** New single-argument `at`, determines its path from the current app mode */
  def at(file:String): Action[AnyContent] = at(modePath, file)
}

The code is pretty self-explanatory, the only "trick" is probably the lazy val which means we only have to evaluate the current operating mode and do the map lookup once.

Now your routes file just looks like this:

GET   /assets/*file     controllers.EnvironmentSpecificAssets.at(file)

OTHER TIPS

Playframework 2.x doesn't support conditional statements in the routes file. The 1.x versions had this but it was removed.

What you have in your routes file is simply two routes with the same URI pattern, /assets/file*. The other lines are just being ignored as comments since they begin with the pound character, #. I think since the pattern is the same for both the first route is catching everything and the second isn't doing anything.

It's not exactly what you're trying to do but I think you can just make the route patterns a bit different and it should work.

GET   /assets/dev/*file   controllers.common.Assets.at(path="/app/assets/ui", file)
GET   /assets/*file       controllers.common.Assets.at(path="/public", file)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top