Scala Web Frameworks: Looking Beyond Lift
Total Page:16
File Type:pdf, Size:1020Kb
The Functional Web Editor: Steve Vinoski • [email protected] Scala Web Frameworks: Looking Beyond Lift Dean Wampler • Think Big Analytics cala is a hybrid object-oriented and func- Zenexity, has worked hard to create a developer- tional programming language for the Java friendly experience. S Virtual Machine (JVM) that’s growing in Installing Play is easy. You download the zip popularity. Two previous Functional Web columns file, expand it in a location of your choosing, and presented the Lift framework, the best-known add the base directory to your environment’s PATH Web framework written in Scala.1,2 In terms of its variable, so the play command is on your path. prominence and full feature set, Lift is the Scala To install the Scala module, run this analog of the Ruby world’s Ruby on Rails. command: But other frameworks exist in the Scala world, just as alternatives exist to Rails in the play install scala Ruby world. One size doesn’t fit all needs. A full list of Scala frameworks is available at http:// Now you can create a Scala Web application in a doi.ieeecomputersociety.org/10.1109/MIC. directory of your choosing: 2011.104. Some are full-stack frameworks for building multi-tier applications. Others are “point” play new SampleScalaApp --with scala tools for specific parts of an application, like tem- play run plate libraries for generating webpages (analog- ous to Java Server Pages). Still others focus on The new application SampleScalaApp is now building particular kinds of networked servers, in a directory of the same name. Play’s built-in like REST response servers that are “headless.” Web server starts via the run command. By Space considerations prevent us from dis- default, it listens for requests on port 9000. If cussing all these tools. It’s hard to choose just a you go to http://localhost:9000 in your browser, few representative examples, but here I focus on you’ll see the page shown in Figure 1, which three: Play, a full-stack, commercially supported provides instructions for what to do next. application framework; Scalatra, inspired by the The directory structure Play creates for an lightweight, popular Sinatra framework; and application will be familiar to Rails programmers. Finagle, a highly scalable, headless server library. Because Play (and Rails) are designed to grow gracefully as applications become large, Play puts Play code for different application responsibilities in Play (www.playframework.org) is a Java-based separate files so file sizes remain manageable. Web framework with a very capable module The SampleScalaApp/app directory has a architecture that makes it straightforward to view subdirectory for views, which hold the write plug-in modules. Scala support is imple- webpage templates, a models subdirectory for mented as a module. It permits the use of Scala domain classes, and a controllers subdirec- throughout the stack, including webpage tem- tory for the responders to user actions. However, plates and the database query layer. because Scala code doesn’t require the direc- A professional Web application developer tory structure to match the package structure, accustomed to the polish and ease of use provided you can put the files for your controllers by Rails will feel at home with Play. Its creator, and models in the app directory, if you prefer. SEPTEMBER/OCTOBER 2011 1089-7801/11/$26.00 © 2011 IEEE Published by the IEEE Computer Society 87 IC-15-05-funw.indd 87 8/16/11 1:02 PM The Functional Web Template('now -> new Date) } def list = { new Template( "contacts" -> Contact.find( "order by name, firstname ASC"). list()) } ... } The sidebar, “An Aside on Scala Syntax” offers a brief explanation of some Scala features used in this and subsequent examples. Figure 1. Your initial Play application webpage. The list method instantiates a new HTML page Template to format the response. The latter is passed GET / Application.index key-value pairs, in which the keys GET /contacts Application.list are names of variables that will be POST /contacts Application.create referenced in the HTML template — in POST /contacts/{id} Application.save this case, a contacts variable. A GET /contacts/{id} Application.form find method on a singleton named GET /contacts/new Application.form Contact, which corresponds to a POST /contacts/{id}/delete Application.delete domain model object of the same name, is called to query the database # Map static resources in /app/public to the /public URL for all the contacts, ordered by name. GET / staticDir:public The query result is converted to a Figure 2. Routing table for ZenContact in the ZenContact/conf/routes file. Scala list. (At the Java byte-code level, The table covers all the life-cycle steps required to view and manage a list of Contact.find will look exactly like a contacts. static find method defined in a tra- ditional Java class named Contact.) The simple examples that come with value appears in this position in an Here is the Contact domain model the Scala module do just that. incoming URL path. The id will be class defined inZenContact/app/ Configuration of various proper- passed to the controller for use as a models.scala (again simplified for ties, such as the database persistence database lookup key, for example. brevity): settings, occurs in SampleScalaApp/ Using the routes from Figure 2, the conf/application.conf. Routing URL URL http://localhost:9000/contacts package models requests to the controllers that handle will get routed to the list method in /* imports ... */ them is defined in SampleScalaApp/ the Application singleton object, conf/routes. which is defined in ZenContact/ case class Contact( Let’s look at the ZenContact sam- app/controllers.scala, which looks id: Pk[Long], ple application that comes with the like this (simplified slightly for @Required firstname: Scala module to see examples of what brevity): String, these various directories and files @Required name: String, might contain. Figure 2 shows the package controllers @Required birthdate: Date, routing table for ZenContact. It cov- /* imports ... */ @Email email: Option[String] ers all the life-cycle steps required to ) view and manage a list of contacts. object Application extends First, the expression {id} defines a Controller { object Contact extends variable id that will be given whatever def index = { Magic[Contact] 88 www.computer.org/internet/ IEEE INTERNET COMPUTING IC-15-05-funw.indd 88 8/16/11 1:02 PM Scala Web Frameworks: Looking Beyond Lift An Aside on Scala Syntax or readers unfamillar with Scala syntax, here are a few • A method definition begins withdef . Types for return val- F pointers: ues are usually inferred, and parentheses are usually omit- • Compared to Java, Scala import statements use the “_” ted if there are no arguments. The method body begins character instead of “*” as a wildcard. after the “=” sign. • Semicolons are inferred. • Scala supports the syntax key -> value to pass key-value • The object keyword declares a singleton object. The run- pairs to maps and methods that want them. time will only instantiate one instance. Scala uses objects to • Pattern matching is like switch statements on steroids. In hold methods and fields that would be declaredstatic in pattern-matching expressions, each potential match begins Java classes. with the case keyword, followed by a match expression • When the case keyword is used, it adds extra features to and the body to execute if the match succeeds. The match a class, including a corresponding singleton object (called a expression and body are separated by “=>”. companion) with the same name (used for factories, pattern • You subclass with the extends keyword. Using the with matching, and so on). keyword, you can implement pure interfaces or mix in addi- • The whole class body is the primary constructor, so the tional behaviors. Both pure Java-like interfaces and mix-ins constructor argument list is passed after the class name. are defined using a feature called traits. You can handle integration with exceptions used in JDBC. Anorm also method will ignore any rows that Play’s Java-based object-relational embraces the view that SQL itself is don’t match one of the cases, effec- mapping (ORM) layer using annota- the best domain-specific language tively implementing a filter. tions (such as the @Required anno- for talking to your database, so you Play provides a rich, well-designed tation on some of Contact’s fields) should embrace it and not try to hide framework for building multi-tier and having the “companion” single- from it. Anorm makes it easy to con- Web applications that will feel ton Contact extend a Magic class vert back and forth between Scala familiar to the Ruby on Rails devel- that provides the find method, for collections and data from queries or oper moving to Scala. The Scala example. data that’s used for updates. You can module adds powerful APIs that So, what are the benefits of using parse results with pattern match- exploit Scala’s functional program- Scala? All the code you would write ing and a built-in parser combinator ming features. in Java becomes more concise in library. Scala, and you gain the additional Here’s an example query adapted Scalatra benefit of Scala’s rich collections from the Anorm documentation: One popular alternative to Rails library. A great illustration of this is in the Ruby world is a lightweight the new Anorm API in Play’s Scala val countries = framework called Sinatra. It’s ideal module (http://scala.playframework. SQL("Select name,population for quickly building lightweight org). It isn’t a traditional ORM, but from Country")().collect { Web applications with minimal a wrapper for the lower-level Java case Row("France", pop:Int) code, where massive scalability and Database Connectivity (JDBC) API.