There’s no doubt it’s much better to use a template or in this case a seed (as outlined in my previous post Starting out with the Playframework (in IntelliJ)) set of code to get us up and running with any application, but I like to know what’s going on with my code, I’m not mad on just leaving it as “magic happens”.
So here’s me, reverse engineering the Play seed and creating my own code from scratch. You should be able to work through each step and at the end, you’ll end up with a single page “Hello World” app.
Lets get all the bits in place…
Creating the Project
From IntelliJ
- Go to File | New Project
- Select Scala | SBT
- Name your project
- Ensure correct Java and Scala versions selected
- Press Finish
Add support for the Play plugin
Select the project node in the package/project (sources root) treeview and right mouse click on it, select new file and name the file plugins.sbt, place the following into it
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.14")
this will add the Play plugins
Edit the build.sbt
Now open build.sbt and add the following below the scalaVersion line
lazy val root = (project in file(".")).enablePlugins(PlayScala) libraryDependencies += filters libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0" % Test
Note: I had to change the scalaVersion to 2.11.11 to get this code to work, obviously it could be a versioning issue on my part, otherwise I got unresolved dependencies.
Click Enable Auto-Import
Add a gradle build file
Add a new file at the same level as the build.sbt and name it build.gradle. Add the following
plugins { id 'play' id 'idea' } task wrapper(type: Wrapper) { gradleVersion = '3.1' } repositories { jcenter() maven { name "typesafe-maven-release" url "https://repo.typesafe.com/typesafe/maven-releases" } ivy { name "typesafe-ivy-release" url "https://repo.typesafe.com/typesafe/ivy-releases" layout "ivy" } } def playVersion = '2.5.14' def scalaVersion = '2.12.2' model { components { play { platform play: playVersion, scala: scalaVersion, java: '1.8' injectedRoutesGenerator = true sources { twirlTemplates { defaultImports = TwirlImports.SCALA } } } } } dependencies { ['filters-helpers', 'play-logback'].each { playModule -> play "com.typesafe.play:${playModule}_$scalaVersion:$playVersion" } }
Configuration folder
Now Add a conf folder at the same level as the project folder (i.e. just select the root, right mouse click, select new directory and name it conf). Select the conf folder, right mouse click and select Mark Directory As… and select Unmark as Resource Root.
Right mouse click on the conf directory, select New File and name it routes (it’s just a text file). Place the following in the file
GET / controllers.IndexController.index
Add another file to conf named application.conf. We’re not actually putting anything in this file.
Create the app folder
Now, again at the root level, create another directory named app and Unmark as Source Root. In this directory add a controllers directory and views.
In app, add a new file named Filters.scala and add the following to it
import javax.inject.Inject import play.api.http.DefaultHttpFilters import play.filters.csrf.CSRFFilter import play.filters.headers.SecurityHeadersFilter import play.filters.hosts.AllowedHostsFilter class Filters @Inject() ( csrfFilter: CSRFFilter, allowedHostsFilter: AllowedHostsFilter, securityHeadersFilter: SecurityHeadersFilter ) extends DefaultHttpFilters( csrfFilter, allowedHostsFilter, securityHeadersFilter )
Add the controllers and views
Okay, before we’ll actually see anything of use we need controllers and views, but in essence at this point you can create a configuration (SBT Task) with the Task run and should be able to run the http server up and see an error as it cannot find the IndexController (at least this gives us the feeling we’re almost there).
Now in the app/controllers folder add a new file named IndexController.scala and place the following code in it
package controllers import javax.inject._ import play.api._ import play.api.mvc._ @Singleton class IndexController @Inject() extends Controller { def index = Action { implicit request => Ok(views.html.index()) } }
and now we need the index view so in app/views add an index.scala.html file and a main.scala.html (this will be our main entry point and the index maps to out IndexController). So the main file should look like this
@(title: String)(content: Html) <!DOCTYPE html> <html lang="en"> <head> <title>@title</title> </head> <body> @content </body> </html>
and index.scala.html should look like this
@() @main("Hello World") { <h1>Hello World</h1> }
Note: the @main passes Hello World through to main.scala.html and that file creates the equivalent of an input argument @(title: String) and (content: Html) the title is what’s passed from index.scala.html in the @main argument.
Now run up the application and using your browser check http://<ip_address>:9000 and you should see the results of your index.scala.html displayed – “Hello World”.
You might feel a little familiar with the @ commands in the html files – these are template commands which the Play Template Engine provides – they’re similar to the likes of Razor and other templating engines.
So for example we might take the request (passed into our IndexController and inject into out html template like this
def index = Action { implicit request => Ok(views.html.index(name = request.rawQueryString)); }
and in the index.scala.html
@(name: String) @main("Hello World"){ <h1>Hello @name</h1> }
Now if we navigate to this http://localhost:9000/?Parrot in our browser, we should see Hello Parrot displayed.
Next steps
Unlike the seed code, I removed all CSS, JavaScript etc. In the seed application off of root we have a public directory with public/images, public/javascripts and public/stylesheet. To make these folders available to our *.scala.html files, we need to add a route to the conf/routes file, for example
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
Now, in our *scala.html files we can access these aseets using code such as
@routes.Assets.versioned("javascripts/main.js")"
here’s the seed main.scala.html file to demonstrate including stylesheets, images and scripts
@(title: String)(content: Html) <!DOCTYPE html> <html lang="en"> <head> <title>@title</title> <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")"> <link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")"> </head> <body> @content <script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script> </body> </html>
Logging
Obviously, once we really get going with our code we’ll probably want to start logging interactions. Play comes with a default logger built in which is as simple to use as this
- import play.api.Logger
- Logger.debug(“Some String”)