Integrating Scalate and Jade with Play 1.2.3
At the beginning of this year, I decided I wanted to learn Scala. Since I'm a Web Frameworks Aficionado, I figured the best way to do that would be to learn Lift. I entered these two items on my todo list and let them lie for a couple months. After attending TSSJS 2011 and having a conversation with James Strachan, I added a couple more technologies to my learning list. James had great things to say about both CoffeeScript and Jade and I decided to learn those as well.
In May, Devoxx announced their Call For Papers and I started reminiscing about how awesome last year's trip was. I decided I'd try to get accepted again and started brainstorming about talks I'd like to give. I came up with "Comparing Scala Web Frameworks" and "HTML5 with Play Scala, CoffeeScript and Jade". The reason I chose Play over Lift for the latter talk is because I think it fits a lot more with the MVC mindset I have and the easy-to-learn nature of web frameworks I enjoy using. Both topics sounded very interesting to me, and I figured they'd also inspire me to learn the technologies in a brute-force fashion; where I was under a time constraint and would be embarrassed in front of a large audience if I didn't succeed.
In mid-July, I got an email from Stephan inviting me to speak again at the 10th edition of Devoxx. I smile splashed across my face and I quickly realized I had a lot to learn. Since I was still in vacation mode after summer vacation in Montana, I decided to wait until I returned from Cape Cod to start studying. While on my 2nd summer vacation, I received an email from Devoxx stating that they'd like me present "HTML5 with Play/Scala, CoffeeScript and Jade".
To learn all these technologies, I decided on an In Anger approach - where I would study minimally and learn mostly by doing. I ordered CoffeeScript on August 8th and Programming in Scala, 2nd Edition the following week (August 17th). I started reading both books while traveling the following week (I found CoffeeScript and Scala to be very similar, so I don't know if I'd recommend learning them at the same time). That same week, I started integrating Scalate (Jade) into a new Play Scala application.
Scalate advertises on their homepage that it works with Play via the play-scalate module. They neglect to mention that this module hasn't been updated in over a year or what version of Play it works with. I tried to use the scalate-0.7.2 version and quickly ran into issues. I posted a message to the Scalate Google Group explaining my compilation issues and stacktraces. The response? Crickets.
Next, I tried posting to the Play Google Group and got a much better response. Here's what they said:
Looking at the Scalate module code, I don't think it can work as is
with Play Scala 0.9.1. The latest version is more than 1 year old, and
we have made a lot of changes in the API.
...
The integration of
Scalate is pretty difficult if you plan to get the same kind of
experience than the native Play scala template regarding auto-reload
and error reports.
You can try to port the module to 0.9.1, basically all it has to do is
to provide a plugin that detect changes to scaml file, and recompile
them. No special integration with the Play API is needed.
After learning that play-scalate was out-of-date, I contacted the project owner via GitHub and tried to get everything working with Play 1.2.3 and Scalate 1.5.1. I updated the dependencies in the project, fixed compilation issues and tried to build. No dice:
build: [mkdir] Created dir: /Users/mraible/dev/play-scalate/tmp/classes [scalac] Compiling 7 source files to /Users/mraible/dev/play-scalate/tmp/classes [scalac] error: class file needed by Binding is missing. [scalac] reference type Serializable of package scala refers to nonexisting symbol. [scalac] one error found
I posted this error to the Play Group, discovered it was caused by Scalate 1.5.1 requiring Scala 2.9. I downgraded to Scalate 2.4.1 and got another nice cryptic error:
build: [mkdir] Created dir: /Users/mraible/dev/play-scalate/tmp/classes [scalac] Compiling 7 source files to /Users/mraible/dev/play-scalate/tmp/classes [scalac] error: class file needed by ScalaController is missing. [scalac] reference value dispatch of package <root> refers to nonexisting symbol. [scalac] one error found
Apparently, this was caused by another Scala versioning issue and I was offered a much simpler solution for integrating Scalate. Below are the steps I performed to integrate Scalate 1.4.1 with Play 1.2.3.
- Updated dependencies.yml to references Scala and Scalate dependencies.
require: - play - play -> scala 0.9.1 - org.fusesource.scalate -> scalate-core 1.4.1: transitive: false - org.fusesource.scalate -> scalate-util 1.4.1: transitive: false
- Added Scalate configuration elements to application.conf.
scalate=jade jvm.memory=-Xmx256M
- Created a ScalateTemplate class to contain the Scalate Engine and render the template.
package controllers import play.Play object ScalateTemplate { import org.fusesource.scalate._ import org.fusesource.scalate.util._ lazy val scalateEngine = { val engine = new TemplateEngine engine.resourceLoader = new FileResourceLoader(Some(Play.getFile("/app/views"))) engine.classpath = Play.getFile("/tmp/classes").getAbsolutePath engine.workingDirectory = Play.getFile("tmp") engine.combinedClassPath = true engine.classLoader = Play.classloader engine } case class Template(name: String) { val scalateType = "." + Play.configuration.get("scalate"); def render(args: (Symbol, Any)*) = { scalateEngine.layout(name + scalateType, args.map { case (k, v) => k.name -> v } toMap) } } def apply(template: String) = Template(template) }
- Created a Scalate trait to override the render() method in Play's Controller class.
package controllers import play.mvc.Http trait Scalate { def render(args: (Symbol, Any)*) = { def defaultTemplate = Http.Request.current().action.replace(".", "/") ScalateTemplate(defaultTemplate).render(args: _*); } }
- Created an Application.scala controller with a default index method.
package controllers import play.mvc._ import models._ object Application extends Controller with Scalate { def index = { render('user -> User("Raible")) } }
The models/User.scala class is very simple:package models case class User(name:String)
- Lastly, I created an index.jade file in views/Application.
-@ var user: models.User p Hi #{user.name}, - for(i <- 1 to 3) p= i p See, I can count!
After getting all this working, I decided it was time to get it into production. As luck would have it, Heroku had just announced Play support a few days earlier. I heard through the grapevine that Play Scala would work, so I gave it a try. It was amazingly easy. All I had to do was create an account, create a "Procfile" in my application's root directory and run a heroku
command followed by a git push
. It all looked great until Play tried to compile my Jade templates as Groovy templates:
Cannot start in PROD mode with errors Template compilation error (In /app/views/Application/index.jade around line 2) The template /app/views/Application/index.jade does not compile : #{user.name} is not closed. play.exceptions.TemplateCompilationException: #{user.name} is not closed. at play.templates.TemplateCompiler.generate(TemplateCompiler.java:102) at play.templates.TemplateCompiler.compile(TemplateCompiler.java:15) at play.templates.GroovyTemplateCompiler.compile(GroovyTemplateCompiler.java:4 1)
The solution from Guillaume was simple enough and I renamed my "views" directory to "templates" and updated ScalateTemplate.scala accordingly. You can see the deployed application at http://play-more.herokuapp.com.
The next steps in my Play Scala adventure will be trying to get the CoffeeScript module to work. I also hope to integrate HTML5 Boilerplate with Jade and Scalate Layouts. I'll be doing this with the mindset that HTML and JavaScript aren't that bad. I expect a lot from CoffeeScript and Jade and hope I enjoy them as much as Strachan.
In the meantime, here's some interesting links I've seen recently encountered that discuss Scala and/or Play. I dig the passion and activity that exists in these communities.
- Yes, Virginia, Scala is hard and the followup.
- Scalatra vs Unfiltered vs Lift vs Play
- Introducing Play 2.0
Posted by Alexy Khrabrov on September 08, 2011 at 02:18 PM MDT #
Posted by opensas on September 08, 2011 at 02:18 PM MDT #
Sorry it took you so long to get play-scalate working; I confess to using Scalate with servlets / JAXRS mostly these days - I've never really done much Play stuff.
Incidentally if it helps, here's a presentation about Scalate (written in Jade + markdown using Scalate to generate the HTML obviously!) which might help give you a quick heads up on Scalate and Jade...
http://scalate.fusesource.org/presentations/scalate#(1)
I hope play 2.0 uses scala 2.9.x so there's no more version conflicts between play & scalate; I wonder if Play or Scalate needs to maintain the play-scalate code base? Sounds like its getting a little bit forgotten...
Posted by James Strachan on September 08, 2011 at 02:18 PM MDT #
Posted by Sakuraba on September 09, 2011 at 01:04 PM MDT #
Finally boarding the Scala train! Good stuff!
While I like CoffeeScript in theory, I am not sure if I like the fact that you don't get to debug the CoffeeScript in your browser, but the JavaScript transpiler result.
Lift is clever and super secure and the Comet support rocks. Eventually, you may want to check out the Bowler Framework and Akka.
Posted by 71.93.198.97 on September 13, 2011 at 02:46 PM MDT #
Hi,
last year I was trying to do the same thing with the scalate module.
I tried to hack the module, it did compile, but lots of errors pop up t runtime.
Good to know that you took another approach to it. by writing your scalate module trait.
Posted by renghen on September 22, 2011 at 12:21 PM MDT #
Posted by Raible Designs on November 02, 2011 at 04:15 PM MDT #
Posted by Raible Designs on November 02, 2011 at 06:23 PM MDT #
Thanks for the article.
I have tried the play-scalate(jade) integration using same steps and it works fine when i run the application from sbt play console.
But somehow it doesn't compile and pack the .jade files when i am creating the zip using dist command at sbt console because of which i am not able to deploy my app at server using the shell script.
Posted by jetinder on May 21, 2013 at 06:59 PM MDT #
Posted by Matt Raible on May 21, 2013 at 07:07 PM MDT #