Scala support
The 1.1 release of play will include support for the Scala programming language. Thanks to the flexibility of the play framework architecture, the Scala support is provided with a simple module. You just need to enable the scala module in the conf/application.conf file.
module.scala=${play.path}/modules/scala
Then you can write all or parts of your play application using scala. You can of course mix it with Java.
We are in very very active development on this stuff. You can try it for now as an experimental feature. Don’t expect to write a complete play application in Scala right now.
For a quick overview of the scala support, you can watch this Scala screencast
Create a new application, with Scala support
You can automatically create a scala ready application, by using the --with option of the play new command. Just try:
play new myApp --with scala
The play application will be created as usual, but if you look at the controllers package, the Application.java file is now replaced by a Application.scala file:
package controllers
import play._
import play.mvc._
object Application extends Controller {
def index = render()
}
It is very close to the Java version of the default Application controller.
Now just run the application as usual using play run and it will display the standard welcome page. Now just edit the Application.scala file to replace the render() call:
def index = "Hello scala !"
Refresh the page, and see the magic.
As always, if you make a mistake, play will just show you the error in a perfect way; (it’s just more difficult now to forget the trailing semicolon)
Direct return types
As shown above, for simple action methods you can directly use the inferred return type to send the action result. For example using a String:
def index = "<h1>Hello world</h1>"
And you can even use the built-in XML support to write XHTML in a literal way:
def index = <h1>Hello world</h1>
If the return type looks like a binary stream, play will automatically use renderBinary(). So generating a captcha image using the built-in Captcha helper can be written as:
def index = Images.captcha
Action parameters, and scala default arguments
You can declare some action parameter the same way you do it in Java:
def index(name: String) = <h1>Hello {name}</h1>
To big plus of scala is the ability to define some default values to these parameters:
def index(name: String = "Guest") = <h1>Hello {name}</h1>
This way if the name HTTP parameter is missing, play will use the default argument value.
Controllers composition using traits
A controller can use several traits to combine several interceptor.
Let’s define a Secure trait:
package controllers
import play.__
import play.mvc.__
trait Secure extends Controller {
@Before
def check {
session("user") match {
name: String => info("Logged as %s", name)
_ => Security.login
}
}
}
And you can them use it in the Application controller:
package controllers
object Application extends Controller with Secure {
def index = "Hello world"
}
How to define and access Models
Models can be defined not only in java but in scala as well. Unfortunately, due to the differences between the two langauges the Model API somewhat differs from the java version.
Main differences
- fields are passed as contructor arguments
- each and every class needs to extend Model[T]
- helper methods should be defined in the companion object
- companion objects need to extend Model[T]
here is an example:
@Entity
class User(
//fields
@Email
@Required
var email: String,
@Required
var password: String,
var fullname: String
) extends Model[User] {
//instance methods
var isAdmin = false
override def toString = email
}
//finder methods
object User extends Model[User] {
def connect(email: String, password: String) = {
User.find("byEmailAndPassword", email, password).first
}
}
Running queries against Scala Models from Scala classes
The following methods are available when running queries against scala models:
def count(implicit m: M[T]) = i.count(m)
def count(q: String, ps: AnyRef*)(implicit m: M[T])
def findAll(implicit m: M[T])
def findById(id: Any)(implicit m: M[T])
def findBy(q: String, ps: AnyRef*)(implicit m: M[T])
def find(q: String, ps: AnyRef*)(implicit m: M[T])
def all(implicit m: M[T])
def delete(q: String, ps: AnyRef*)(implicit m: M[T])
def deleteAll(implicit m: M[T]) =
def findOneBy(q: String, ps: AnyRef*)(implicit m: M[T]): T
def create(name: String, ps: play.mvc.Scope.Params)(implicit m: M[T]): T
As you can see, it’s really similar to the java API, so for example to count the number of users, you can just call count on the User class:
User.count
One known limitation of the Scala Model API is that the save method is not working in a chained call fashion, so you always need to execute it on an instance, as you can see it later at the unit testing section
Running queries against Java Models from Scala classes
In certain situations it might be desirable to query models written in java from scala.
Since java models are not extending from the scala Model trait, Play needs to provide an alternative query interface which comes in the form of QueryRunner trait and the corresponding companion object. In order to utilize this feature you either need to import the query methods like
import play.db.jpa.QueryRunner._
or you can mix in the trait
class MyController extends Controller with QueryRunner {...}
and the API is defined like this:
def count[T](implicit m: M[T]) = i.count(m)
def count[T](q: String, ps: AnyRef*)(implicit m: M[T])
def findAll[T](implicit m: M[T])
def findById[T](id: Any)(implicit m: M[T])
def findBy[T](q: String, ps: AnyRef*)(implicit m: M[T])
def find[T](q: String, ps: AnyRef*)(implicit m: M[T])
def all[T](implicit m: M[T])
def delete[T](q: String, ps: AnyRef*)(implicit m: M[T])
def deleteAll[T](implicit m: M[T]) =
def findOneBy[T <: JPASupport](q: String, ps: AnyRef*)(implicit m: M[T]): T
def create[T <: JPASupport](name: String, ps: play.mvc.Scope.Params)(implicit m: M[T]): T
Using the previous example User.count becomes count[User]
Unit Testing
ScalaTest support is integrated into Play, so one can easily write unit tests using ScalaTest, for example:
class SpecStyle extends UnitTest with FlatSpec with ShouldMatchers {
"Creating a user" should "be succesfull" in {
val user = new User("[email protected]", "secret", "Bob")
user.save
bob = User.find("byEmail", "[email protected]").first
bob should not be (null)
bob.fullname should be ("Bob")
}
}
Tutorial
We are currently writing a version of the play tutorial for scala.
Read The play tutorial (Scala version).