Chapter 7. Connecting to a database

This chapter covers

  • Setting up a database from SBT
  • Connecting to a database from weKanban using Squeryl
  • Finishing the weKanban application

In chapter 6 you learned how to create a simple web application using the Simple Build Tool (SBT) and the Scalaz HTTP module. But the application you set out to build in the previous chapter wasn’t complete. The reason: to build a functional Kanban application, your application needs to store information such as stories and its status into persistent storage.

Note

This chapter is an extension of chapter 6, so if you haven’t read that chapter, some parts in this chapter related to Scalaz and SBT could be hard to follow.

In this chapter you’ll complete the weKanban application started in chapter 6. You’ll learn how to retrieve and store information in a relational database. I introduce a Scala Object Relational Mapping (ORM) tool called Squeryl to communicate with the database. You’ll also explore how to model database tables in a type-safe manner. You’ll build a new screen for adding new stories to the application and a screen that displays all the stories added to the Kanban board. In the process of building these screens, you’ll explore how to work with databases from Scala applications. Even though the focus of the chapter is working with a database, I will show you bits of Scalaz and SBT that are required to connect all the pieces. Before building our application, let’s recap all the stories you need to implement the complete weKanban application:

As a customer I want to create a new user story so that I can add stories to the ready phase.

As a developer I want to move cards (stories) from one phase to another to signal progress.

Let’s start by building a screen that will allow users to add a new story to the weKanban board.

7.1. Adding a new story to a weKanban board

The first thing to work on is adding a new story to the board, because without that it would be difficult to do anything with the board. Adding a story means you have to worry about the persistence store. Enterprise developers use relational databases to persist information in tables, and I suggest using the open source Java SQL database called H2 (www.h2database.com/html/main.html). Actually, you’re free to pick any of the following databases: Postgres, Oracle, MySQL, and DB2. The reason I restrict you to this predefined list is because the Scala Object Relational Mapping (ORM) library you’ll use for your application, called Squeryl (http://squeryl.org/index.html), can only support those databases at the time of writing.

Note

Using a schema-free database like MongoDB could be argued for this application, but I want to focus on a more traditional relational database solution to help you see how to work with the relational database management system (RDBMS) in Scala. You’re free to experiment with other types of databases.

7.1.1. Connecting to a database using Squeryl

So why use Squeryl to access a database? First it’s popular in the Scala community. It also provides a nice, simple DSL to talk to a database. Even though it’s fine to use JDBC directly from Scala, you’ll use Squeryl to learn an ORM tool that’s completely written in Scala. Scala’s strong type system is perfect for creating a type-safe ORM tool. I encourage you to play with other Scala ORM tools like ScalaQuery[1] and Querulous[2] to know your options.

1 A fork of SLICK to keep old links to the ScalaQuery repository alive, http://github.com/szeiger/scala-query.

2 Querulous, an agreeable way to talk to your database, http://github.com/nkallen/querulous.

For now, add Squeryl as an SBT dependency to WeKanbanProjectDefinition.scala—and while you’re there, add H2 as well, as shown in the following listing.

Listing 7.1. Complete weKanban build.sbt project definition

By now you know what you have to do to update your SBT dependencies. For your Kanban board the story should have three attributes: a story number that identifies the story uniquely, a title describing the story, and the phase the story is in. The following is how you represent a story class in Scala:

class Story(val number: String, val title: String, val phase: String)

To make this class work with Squeryl, you have to do a couple of simple setups. First you have to tell Squeryl you need a table that will store all the stories for you in the database. The way to do that in Squeryl is to create a subclass of org.squeryl.Schema. Think of this class as equivalent to a database schema where you’ll keep all the data definitions of an application. The following code defines the schema with a table called “STORIES” for your Story class:

package com.kanban.models

import org.squeryl._

object KanbanSchema extends Schema {
  val stories = table[Story]("STORIES")
}

Save this as the file KanbanSchema.scala under src/main/scala/com/kanban/models. One thing to note here is I’m defining the table in a type-safe manner. The stories value now represents the database table “STORIES,” and you can invoke various types of queries on the stories object without worrying about type and always get back story type objects.

Note

Most ORM tools use some kind of external configuration file to specify schema and domain model mapping information. But because Scala is a DSL-friendly and expressive language, it’s common for Scala tools to use the Scala language itself for configuration. You’ve already seen examples of this in SBT and now in Squeryl. The next time you think you need to have an external configuration/properties file, think how you can express that in the Scala language.

Next, configure Squeryl to use the H2 database. Before connecting to the database, make sure that the H2 database server is running. It’s simple to start the H2 server—all you have to do is provide the h2 .jar files in the path:

java -cp ~/.ivy2/cache/com.h2database/h2/jars/h2*.jar org.h2.tools.Server

This is a little ugly because you have to dig into the location where SBT stores all your runtime dependencies (ivy cache). It would be better if you could start and stop the H2 server as you can for the Jetty server. Unfortunately SBT doesn’t come with built-in support for H2, so you have to create new tasks for H2. The good news is that it’s easy to add new build tasks to SBT, but for that we have to make changes to the build.scala file. SBT provides lots of helper methods to create custom tasks, but here the tasks are implemented by methods in build.scala. Copy the code from the following listing into your build.scala file.

Listing 7.2. build.scala file to define a custom task

The build.scala file is doing a couple of things here. First it defines the project by providing name, locations, and settings. The project inherits settings from the build definition by default and along with that I’ve added two new tasks: startH2Task and stopH2Task. These tasks are now part of the project settings and can be used to start and stop the H2 database.

The second thing is the versions of the scalazVersion and jettyVersion are declared as a lazy val. The benefit of this is you don’t have to repeat the version number multiple times when declaring dependencies in the build.sbt file:

libraryDependencies ++= Seq(
  "org.scalaz" %% "scalaz-core" % scalazVersion,
  "org.scalaz" %% "scalaz-http" % scalazVersion,
  "org.eclipse.jetty" % "jetty-servlet" % jettyVersion % "container",
  "org.eclipse.jetty" % "jetty-webapp" % jettyVersion % "test, container",
  "org.eclipse.jetty" % "jetty-server" % jettyVersion % "container",
  "com.h2database" % "h2" % "1.2.137",
  "org.squeryl" % "squeryl_2.10" % "0.9.5-6"
)

Yes, you can share the settings and vals from the build.scala file to build.sbt files. In fact it’s a common practice to declare common things in build.scala files and use them in build.sbt files. In the end, all the settings from various files are combined into one sequence of settings.

After the build definitions are reloaded, you’ll see two new tasks, h2:start and h2:stop:

> h2:st
start   stop

How h2:start and h2:stop tasks are implemented

Creating new tasks in SBT is simple: create a TaskKey and assign a closure that implements the task. Because you want to play nice with other custom tasks and plug-ins, a new scope is created for H2 tasks:

lazy val H2 = config("h2") extend(Compile)

This line creates a new config name “h2” and extends the Compile config. The Compile config will provide the necessary classpath setting you need to run the tasks. The new config will create a new scope for you, and the tasks will be available under it.

The startH2 task is implemented by the startDatabase method. This method expects a sequence of paths that point to the H2 database .jar files you need to start the database. Because the H2 config extends Compile config, you can easily use the fullClasspath setting at Compile scope to tap into the classpath. And the <<= method in SBT helps to create a new setting that depends on other settings. The following code snippet maps the path information from fullClasspath in Compile scope and creates a new function that will get executed for the start task:

val startH2Task = startH2 in H2 <<= (fullClasspath in Compile) map {
          cp =>
    startDatabase {
      cp.map(_.data)
        .map(_.getAbsolutePath())
        .filter(_.contains("h2database"))
    }
  }

The startDatabase method stores the reference of the process object so that it can be used in the stopDatabase method. The stopDatabase method is associated with the h2:stop task.

This will help you in working with H2 without leaving the comfort of the SBT console. When you run h2:start, it will automatically start the H2 database server at port 8082 and open your default browser with a login screen, as shown in figure 7.1.

Figure 7.1. The H2 console

(If the browser doesn’t open, try going to http://localhost:8082 directly from the browser.) Because the H2 server is running, let’s switch focus to make Squeryl connect to this running server. To connect to the H2 server, use the following driver and database URL:

  • JDBC Driver class: org.h2.Driver
  • Database URL: jdbc:h2:tcp://localhost/~/test
  • User name: sa

All this information and more is available in the H2 documentation (www.h2database.com/html/quickstart.html). The following listing adds an init method to the KanbanSchema class to connect to the H2 database.

Listing 7.3. KanbanSchema with init method for database connection

The KanbanSchema represents the schema definition of the weKanban application. The first thing to do is map the Story DOM with the STORIES table in the database using the table method defined in the Squeryl Schema class.

The init method is responsible for establishing a connection with the running H2 database. In the init method, import org.squeryl.SessionFactory. SessionFactory is similar to the database connection factory and is used in Squeryl to create new connections. Next, load the Java Database Connectivity (JDBC) driver for the H2 database using Class.forName("org.h2.Driver"). This driver will be used when you create a new database connection.

Creating a new connection in Squeryl means creating a new Session. This approach is similar to the popular ORM mapping tool in Java called Hibernate (www.hibernate.org), where the connection is encapsulated as Session. Think of a Squeryl session as a wrapper to a database-based connection with which you can control database transactions. The Squeryl Session instance provides additional methods like log and methods for binding/unbinding the session to the current thread. Note that Session saves the database connection to a thread local[3] variable so that each thread in the application gets its own connection. This is useful in web applications where you can have multiple users accessing your application at any point.

3 “Class ThreadLocal<T>,” Java Platform Standard Ed. 6, http://mng.bz/cqt0.

In Squeryl, the mechanism for creating new sessions needs to be defined in a variable called concreteFactory, defined in the SessionFactory object. By default the value of this variable is None. If the value of the concreteFactory is something other than None, you know it’s initialized. And Squeryl expects concreteFactory to be a function that will create new sessions. In this case the function looks like the following:

()=>
   Session.create(
     DriverManager.getConnection("jdbc:h2:tcp://localhost/~/test",
                              "sa", ""), new H2Adapter))

Here you’re calling the utility method defined in the Session object called create by passing a database connection and the adapter. The Java DriverManager takes the connection URL to the H2 database, username, and password to create a new connection. Squeryl defines adapter classes for each supported database type, and in this case you’re using H2Adapter for the H2 database. Because the type of the concreteFactory is Option[()=>Session], you need to wrap your function with the Option value Some.

Because you’ve defined the stories object to represent the "STORIES" table, this won’t create tables in the database. You have to explicitly do that. In some cases you’d rather create database tables using SQL scripts, but here you’ll use Squeryl to create the schema for you. Do that by adding a main method to your KanbanSchema class so that you can use SBT to run it whenever you need it:

The inTransaction method defined by Squeryl runs the given closure in a database transaction. It creates a new transaction if none is in progress. Here in the transaction block, you’re dropping all the tables and creating them back again. Right now the only table defined is "STORIES." Now when you execute the SBT run build action, it will invoke the main method and will create a fresh schema for you. Before running this action, make sure your H2 database is running (you can use h2:start to launch the H2 server). Now let’s move on to saving a new story to the database.

7.1.2. Saving a new story to the database

To insert a row into the database using Squeryl, you have to call the insert method defined in the org.squeryl.Table class, which takes an instance of a model and saves it to the database. You already have a table object called stories in your WeKanban-Schema object that points to the “STORIES” table in the database. If you create an instance of Story and pass it to the insert method, you can save a Story to the database. So far the Story class looks like the following:

class Story(val number: String, val title: String, val phase: String)

Before saving an instance of Story to the database, you have to add validation. For example, both the number and title properties of Story should be nonempty, and because the story number should uniquely identify a story, you have to make sure that the number value is unique too. Checking whether a field is empty is simple; here’s how it’s implemented:

class ValidationException(message: String) extends RuntimeException(message)

private[this] def validate = {
    if(number.isEmpty || title.isEmpty) {
      throw new ValidateException("Both number and title are required")
    }
}

Add this validate method to the Story class and invoke it before saving it to the database. Here you’ve created a custom exception called ValidatationException that you’ll use for all the validation failures.

Note

You didn’t define any primary key for your Story class but instead used number as a unique key. This is okay for the small application you’re building here, but in the real world you should have a surrogate key as primary key for your model classes. To add an autoincrement id field to your domain class, you can extend the KeyedEntity[A] trait. You can also use KeyedEntity to create composite keys. For more information, see the Squeryl documentation.

To check the uniqueness of the number field, you have to query the "STORIES" table to make sure there’s no other story with the same number. Squeryl provides a nice method called where in table objects that you can easily use to achieve that. The where method takes a predicate function to filter out rows from the result. Here’s how to check the uniqueness of a story number using the where method:

if(!stories.where(a => a.number === number).isEmpty) {
   throw new ValidationException ("The story number is not unique")
}

Here using the function a => a.number === number (=== is the equality operator defined by Squeryl), you’re only selecting the stories that match the given story number. If that results in anything other than an empty collection, then the given number isn’t unique. Note that the where method returns a lazy iterable called Query defined by the class org.squeryl.Query. The query is only sent to the database when you start the iteration. After adding this validation, the validate method now looks like the following:

private[this] def validate = {
    if(number.isEmpty || title.isEmpty) {
      throw new ValidationException ("Both number and title are required")
    }
    if(!stories.where(a => a.number === number).isEmpty) {
      throw new ValidationException ("The story number is not unique")
    }
}

Now, before inserting the new story into the database, you’ll invoke this validate method to make sure the story is valid. You also have to run under a database transaction so you can commit or roll back your changes when saving the story instance (more on this later). For now, let’s add a method called save to our Story class. But what should you return from the method? Well, you could return a success message that the story is created successfully, but what if something goes wrong while saving to the database? In those cases I prefer scala.Either (discussed in chapter 4), which allows you to return both success and error responses. This will also help the caller of the save method to handle both scenarios gracefully (you’ll see that shortly). Here’s the complete Story class after adding both the validate and save methods.

Listing 7.4. Story class with validate and save methods

Here in the Story class you added two new methods, validate and save. Inside the validate method you do a couple of validation checks. First you’re checking whether the story number or title is empty because neither can be empty. If either is empty, you throw the ValidationException created in . The second validation involves going to the database and making sure that the given story number is unique. Here you’re using the built-in method called where, available to all table objects. The where method takes a function that filters out stories based on a Boolean expression (similar to the filter method defined in the Scala collection library). In your function you’re matching on story number a => a.number === number, where a represents a story saved in the database. If the where method results in a nonempty collection, you throw ValidationException for a nonunique story number.

The save method first calls the validate method to make sure the story is valid and then invokes the insert method defined in the stories table object to save it to the database. Both method calls are wrapped in a closure of the tx method. The tx method is responsible for initiating SessionFactory.concrete and the database transaction. This method is defined in KanbanSchema, and you’ll see it shortly. Because save could result in both success and failure, I’m using scala.Either as a return type of the save method. This helps to communicate to the caller of the method that expects both success and failure. In Scala, using scala.Either is a more common idiom than throwing exceptions from public methods. Additionally, you created a companion object for Story to create new story instances. The default phase for a new story is “Ready,” because that’s the first phase in the Kanban board.

The tx method in the previous code snippet makes sure that the Squeryl SessionFactory is initialized properly and starts a new transaction if no transaction exists. The tx method takes a function as a parameter and returns the response of the function. This function could be any closure or block of code that you want to run within a transaction boundary. The following listing shows the complete KanbanSchema object.

Listing 7.5. Complete KanbanSchema object

The tx method takes a function and runs that function in the transaction. The inTransaction method defined by Squeryl checks whether there’s any transaction in progress and, if so, it participates in the in-progress transaction—otherwise it creates a new one. The tx method first invokes the init method to make sure that SessionFactory.concreteFactory is initialized properly before initiating the transaction. The inTransaction will roll back if the given function throws an exception—otherwise the transaction is committed if there’s no in-progress transaction. In case of an in-progress transaction, the given function will be executed and the result returned. The init method is explained in listing 7.2.

7.1.3. Building the Create Story web page

In this section you’ll build the screen with which the user will create a new story and add it to the Kanban board. You’ll also hook your Story model object with the input from the screen and complete the following feature of the weKanban application:

As a customer, I want to create a new user story so I can add stories to the ready phase.

You can create dynamic web pages in Scala in many ways. You can use JSP, the Scala template engine (Scalate[4]), or Scala’s built-in support for XML to generate XHTML. Here you’ll use Scala’s XML support to your advantage to generate XHTML web pages. It’s simple and testable and will demonstrate some of Scala’s XML capabilities (covered in chapter 2). For complex and large applications, this approach doesn’t scale. In chapter 12 you’ll explore Scala web frameworks that make building large web applications easy.

4 “Scalate: Scala Template Engine,” Scalate 1.5.3, http://scalate.fusesource.org.

To represent each screen as XML, you’ll create a view object that will be used by your WeKanbanApplication (see listing 5.3) class when sending the response. Figure 7.2 shows what the Create a new Story screen looks like.

Figure 7.2. The Create a new Story screen

To create the screen in figure 7.2, see the following listing to create a CreateStory object under src/main/com/kanban/views.

Listing 7.6. CreateStory view object

Here, in the apply method of the view object, you have the necessary HTML that when rendered will create a view like figure 7.2. Even though it’s HTML, it’s also valid XML or XHTML and can be used easily as an XML fragment inside Scala code. The return type of the apply method is scala.xml.NodeSeq, which is a sequence of XML nodes, and when rendering the string representation of the NodeSeq will return exact HTML code. Now let’s tie this view to a URL so you can render this view. Copy all the static resources like CSS and JavaScript files from the accompanying code base of this book. The main.css file should go in the webapp/css folder, and JavaScript files should go in the webapp/js folder.

Note

To save typing, you can copy view objects from the code available for download at https://github.com/nraychaudhuri/scalainaction.

So far in your Scalaz application class you’ve handled static resources using the resource method:

def application(implicit servlet: HttpServlet, servletRequest:
     HttpServletRequest, request: Request[Stream]) = {
   def found(x: Iterator[Byte]) : Response[Stream] = OK << x.toStream
   resource(found, NotFound.xhtml)
}

To handle dynamic resources like view objects, create a method called handle in the application class. This method will take the same parameters as the application method but will match on the URL on the request object. Typically web frameworks use a separate configuration file to map a URL to a resource or function. In convention-based frameworks like Rails, the playframework URL contains enough information to map to the appropriate function or action. Scalaz takes a different approach—it uses Scala’s powerful pattern matching, where the URL is broken into an HTTP method and URL parts as List. For example, a request object with the URL http://localhost:8080/card/create can be matched like this:

request match {
   case MethodParts(GET, "card" :: "create" :: Nil) => ...
   ...
}

The MethodParts is an extractor object (see section 3.7.1) that takes a Scalaz Request and returns Option with the HTTP method and URL parts as List. In the previous code snippet, GET is the HTTP method used to request the resource, and the second parameter is the URL broken into the List.

How an Extractor object works

In chapter 3 you learned how to use case classes for pattern matching, but pattern matching isn’t restricted to case classes. You can use almost any object for pattern matching as long as it defines a method called unapply. Any object with the unapply method is called an Extractor object. For example, MethodParts in Scalaz is defined as the following:

object MethodParts {
   def unapply[IN[_]](r : Request[IN]) : Option[(Method,
    List[String])] = {
     Some(r.method, r.parts)
   }
 }

Here the unapply method takes an instance of the Scalaz request and returns a Some value of method and URL parts. The parts method returns all URL path elements separated by /.

When Scala encounters the pattern case MethodParts(...), it translates that to an invocation of MethodParts.unapply by passing the reference to the object that’s used to match the pattern (in this case it’s an instance of Scalaz request). Note that the apply method isn’t necessary for pattern matching. It’s typically used to mimic constructor calls. For example, you’re using the apply method in the Story object to create a new instance of the Story class.

One rule to notice here is if you want to return some value from unapply, it then needs to be wrapped around the scala.Option type.

Sometimes you’ll request this resource by passing a request parameter. For example, when a story is created successfully, you’ll come back to the same “Create story” page but with a success message. To read a request parameter from a URL, use the ! method defined in the Scalaz Request object. Let’s create a private method called param in your application class that will return a string value of the parameter or an empty string if the parameter isn’t specified:

def param(name: String)(implicit request: Request[Stream]) =
     (request ! name).getOrElse(List[Char]()).mkString("")

The ! method of Scalaz Request returns Option[List[Char]] and you’re converting that to a string. Now let’s create the handle method by combining these pieces:

def handle(implicit request: Request[Stream],
     servletRequest: HttpServletRequest): Option[Response[Stream]] = {

    request match {
      case MethodParts(GET, "card" :: "create" :: Nil) =>
        Some(OK(ContentType, "text/html") << strict <<
      CreateStory(param("message")))

     case _ => None
    }
}

Here, when the request matches both the HTTP method type and the URL, you’re creating a response by invoking the CreateStory view object by passing the value of the request parameter message. OK(ContentType, "text/html") creates an empty Scalaz Response with the HTTP response header content-type represented by the Content-Type object. The << method allows you to add additional information to the response object. And because Scala encourages creating immutable objects, every time you call the << method, a new response object is created.

Note

The strict used in the handle method is called the doctype. The doctype declaration isn’t an HTML tag. It’s an instruction to the web browser about what version of the markup language the page is written in. To adhere to strict HTML standards, the example is using the strict doctype.

In the application method (your entry point for all URLs) you’ll invoke your new handle method. You can still keep the existing resource method so you can load static resources. The Scalaz core provides a method called | for the Option class and using it you can combine both handle and resource methods so that when the handle method returns None you can invoke the resource method as a fallback to load resources. Here’s how the application method looks after changes:

def application(implicit servlet: HttpServlet,
     servletRequest: HttpServletRequest, request: Request[Stream]) = {

   def found(x: Iterator[Byte]) : Response[Stream] = OK << x.toStream
   handle | resource(found, NotFound.xhtml)
}

Because all the parameters to the handle method are implicit, you don’t have to explicitly pass them, but you can. If the handle method returns None as a response, the resource method will be called, and you do that with the | method. If no resource is found matching the URL, then NotFound.xhtml is returned.

Note

Scalaz uses Scala’s implicit method conversion to add the | method to the Option class. If you’re used to metaprogramming in Ruby, Groovy, or other programming languages, implicit conversions are Scala’s way of doing metaprogramming but in a more controlled way. You’ll explore implicit conversion in detail in the next chapter.

Now before going ahead with running the application, take a look at the following listing, showing the WeKanbanApplication class you have so far.

Listing 7.7. WeKanbanApplication with the handle method for creating a story

Here you added two new methods to the existing WeKanbanApplication class to handle a new story request from the browser. The handle method matches the HTTP request to a function. So far, it only knows how to handle the create story request. Here you’re using the Scalaz Extractor object called MethodParts, which breaks the Scalaz request into request type and URL parts. The HTTP GET request to the http://localhost:8080/ card/create URL will be matched to MethodParts(GET, "card" :: "create" :: Nil), where GET is the method type and "card" :: "create" :: Nil are the parts of the URL. And when it matches, it creates a new Scalaz response to render the create story screen using Some(OK(ContentType, "text/html") << strict << CreateStory(param("message"))). CreateStory is the name of the view object, and OK(ContentType, "text/html") << strict creates an empty Scalaz response with a strict HTML doctype.

The param method retrieves the parameter value for a given parameter name. You’re using the ! method defined in the Scalaz request to retrieve the parameter value and transform into a String. Scalaz by default uses List[Char] to represent a parameter value.

When the requested URL doesn’t match the cases defined in the handle method, it returns None (default case). In case of None, the application method calls the resource method to load any static resource that matches the requested URL.

Now go to your SBT console and start the Jetty server using the jetty-run build action if it’s not already running. This will start the server at port 8080. If you go to the http://localhost:8080/card/create URL, you’ll see a screen similar to figure 7.2. That’s great, but you still have to tie the Save button with the save method of the model object.

Clicking Save will result in an ugly error because you haven’t linked the URL to any function in your application class. Fix that by adding a MethodParts to your handle method, which will match the URL ending with /card/save to the saveStory method defined in the application class:

The saveStory method will read the HTTP POST parameters from the request, instantiate an instance of the Story model class, and invoke the save method (see listing 7.3) on it. To read POST parameters from the request, add another utility method like the param method to your application class, but this time with a ! because POST generally means a side-effect (section 5.1):

def param_!(name: String)(implicit request: Request[Stream]) =
     (request | name).getOrElse(List[Char]()).mkString("")

You know that the save method (see listing 7.3) in the Story class returns scala.Either[Throwable, String], and in case of error it returns Left with the exception; otherwise Right with the success message. Left and Right are the only two subtypes of Either. You can easily use pattern matching and take appropriate actions. When save is successful, you’ll redirect to the “Create story” screen with a success message so the user can create another story; in case of error you return to the “Create story” page, but this time with an error message. The following is how to implement the saveStory method:

The redirects method is defined in the Scalaz Response object, which is already imported for the application class. The redirects method takes the relative URL and the parameters to the URL as a tuple.

Note

It’s always a good idea to redirect after a POST form submission to avoid double submission.

Before testing the save method, make sure the H2 database server is running (you can start H2 server using h2:start task). The next listing shows the complete application class you have so far.

Listing 7.8. Complete save story in WeKanbanApplication

Here you’re extending the application class to handle the save story POST request. To handle the save story request, you added a new pattern matching expression to the handle method. Now the POST request made to /card/save will be matched by MethodParts(POST, "card" :: "save" :: Nil), and when it matches it invokes the saveStory method defined in the application class.

The saveStory method extracts the story number and title parameters from the Scalaz request and creates a new instance of the Story model object. The save method defined in the Story model object will validate and save the story in the database. In case of error, the saveStory method renders the CreateStory view object with the error message. When the save is successful it redirects to a new “Create story” screen. Here using the scala.Either type allows you to easily handle the error condition.

You’re done adding new stories to the Kanban board. Next you’ll build the Kanban board where all the stories will be displayed.

7.2. Building the Kanban board page

Now the focus will move to building the Kanban board. Your next user story:

As a developer I want to move cards (stories) from one phase to another so that I can signal progress.

Figure 6.1 shows the prototype of the Kanban board you’re supposed to build. To implement this story, you have to provide an ability to move cards from one phase to another. For example, a user of your Kanban board should be able to move a card from ready phase to development phase and vice versa. To implement drag-and-drop, use the jQuery-ui plug-in (http://jqueryui.com) for jQuery (http://jquery.com), a JavaScript framework that simplifies HTML, document traversing, event handling, and Ajax interactions for web development.

I don’t focus on the jQuery library in detail here, but if you haven’t used jQuery I encourage you to check the links. After you download the jquery-ui library, copy the JavaScript files to the js folder under the webapp folder of the weKanban project, as shown in figure 7.3. The main.js file is something you’ll create shortly.

Figure 7.3. The weKanban project with JavaScript files for drag-and-drop

Note

You can also copy the JavaScript files from the code that accompanies this book.

The draggable plug-in of jQuery adds draggable functionality to any DOM element, and all you have to do is invoke a draggable method to it. For example, if you want to make your stories draggable, invoke the draggable method on each DOM element that represents a story in the board. For now, you’ll add a css class called story to each story to easily find them and make them draggable.

To make DOM elements droppable, call the droppable method by passing a function that can handle the drop event. In your board all the elements that represent phases (ready, dev, and so on) will be droppable.

But only implementing drag-and-drop in the web page isn’t enough—you also have to update the database when a story moves from one phase to another so that when you come back to the board you can see the updated version. Because you’ve already decided to use jQuery, you’ll use its Ajax features to make an HTTP POST call when a story moves from one phase to another. The next listing shows the completed main.js file that implements drag-and-drop for the Kanban board.

Listing 7.9. Implementing drag-and-drop for the weKanban board in the main.js file

In the init function you’re making the element with the story class draggable and adding droppable functionality to all the phases identified by their ids. To implement droppable you’ve implemented the drop function, which gets invoked when an element is dropped. Inside the drop function you’re calling the moveCard function by passing the id of the story that’s dropped and the target phase. The job of moveCard is to make an Ajax POST call to the /card/move URL by passing both the story number and target phase. Now you’ll create the view object for the Kanban board and the JavaScript code.

7.2.1. Creating the view for the Kanban board

To create the Kanban board view you have to retrieve all the stories from the database by phase. You have to show all the stories that are in the ready state, all the stories at dev state, and so on. At first all the stories that you create using the “Create story” view will be in the ready state. But once you implement the drag-and-drop features, you’ll have stories in various phases.

At first, to select stories by phase, add a findAllStoriesByPhase method to the Story model object which will return a list of stories which you’d use to render the view. To implement the findAllStoriesByPhase method, use Squeryl’s query DSL, which looks similar to SQL. First use Squeryl to find all the stories from the database and then apply a filter to the result to find stories matching a particular phase. To find all the stories from the database using Squeryl, you have to do the following:

from(stories)(s => select(s))

Here the from method returns an instance of Query, which takes a function that takes an instance of the story object and returns the same story object as select. But this isn’t helpful because you want to filter the stories by phase; to do that, add a where method that will check for the phase, as in the following:

from(stories)(s => where(s.phase === phase) select(s))

The === method is added by an implicit method conversion to a String class so that the DSL looks like SQL’s where clause. The Squeryl query returns an immutable instance of Query that’s a lazy Iterable collection. At this point Squeryl has only created the query—it hasn’t executed it in the database. It will execute the query the moment you try to access the first element in the collection. But because you’re going to use this collection to render stories in your Kanban board view, let’s change this to strict collection from lazy collection (covered in chapter 4) by invoking the map method, so that you access these instances of Story objects outside the transaction:

def findAllByPhase(phase: String) = tx {
  from(stories)(s => where(s.phase === phase) select(s)) map(s => s)
}

Here you’re calling map on the Query object to create a collection of stories. But because you don’t need to transform your story objects, the map is returning the parameter.

Note

You could use a different method toXXX methods defined in the Scala Iterable trait to transform to various types of collections. Here I’m using map to demonstrate that you can take the response from Squeryl and transform it if necessary.

Now in your view object you can invoke the findAllByPhase method by passing various phases to render the stories in the Kanban board. To create the view for the Kanban board, add a new view object under src/main/scala/com/kanban/views/ KanbanBoard.scala.

package com.kanban.views

import com.kanban.models._

object KanbanBoard {
}

The first thing you’ll do is add a header method that will include all the JavaScript files you need to enable the drag-and-drop feature to your board:

This header method, apart from adding all the JavaScript, also invokes the init method that’s defined in the main.js to initialize the drag-and-drop functionality using jQuery. To render the stories you get back from the findAllByPhase method, add another method that will loop through the stories and create an html div element:

private def stories(phase: String) =
    for(story <- Story.findAllByPhase(phase)) yield
      <div id={story.number} class="story">
        <fieldset>
          <legend>{story.number}</legend>
          <div class="section">
            <label>{story.title}</label>
          </div>
        </fieldset>
      </div>

Here you’re using a for-comprehension to loop through all the stories and creating a story html div element. This method returns a list of scala.xml.NodeSeq objects that you can easily insert into the body of HTML you’re going to generate for the Kanban board. To tie all these pieces together, add the apply method to the KanbanBoard view object, which will create the Kanban board view, shown in the following listing.

Listing 7.10. Kanban board view

The KanbanBoard view object is used to render the Kanban board in figure 7.4. Like the CreateStory view object, the apply method is responsible for rendering the Kanban board. The apply method calls the header method to add all the JavaScript files you need to add drag-and-drop functionality to your Kanban board. The contents of the header method get inserted in between the HTML head tags.

Figure 7.4. Kanban board with stories in ready phase

To render stories in each phase, the apply method invokes the stories method by passing the phase. The stories method is invoked for each phase in the Kanban board. The stories method uses a for-comprehension to generate the HTML required to render the result. The findAllByPhase method in the Story model class returns all the stories in a given phase from the database.

The last missing piece is to modify the handle method in the WeKanbanApplication class to handle the /kanban/board URL. Add the following case to the handle method:

case MethodParts(GET, "kanban" :: "board" :: Nil) =>
        Some(OK(ContentType, "text/html") << transitional << KanbanBoard())

If you rebuild and run the application again (if you’re running ~prepare-webapp, the changes will be available to you automatically), the link “Go to Kanban board” will take you to the Kanban board screen displaying all the stories you created (figure 7.4).

Even though you haven’t implemented the move card functionality on the server side, in the UI you should be able to drag and drop cards from one phase to another. Because the server side logic isn’t implemented, when you refresh the page all the stories will show up in ready phase again. To complete the move card story, you have to implement the move card logic in the server side.

7.2.2. Moving cards in the Kanban board

To implement moving cards from one phase to another, you have to update the phase of the story. For example, to move a story from the ready to the dev phase, you have to get the story number (the story number uniquely identifies a story) to find the story and update the phase. But the problem is, you aren’t allowed to cross the limit set for the phase, so before updating the phase you have to check whether you’re crossing the threshold of the phase you’re moving to. The limit for ready is 3, for dev it’s 2, and for test phase it’s 2 stories. To validate that you aren’t crossing the limit, add a validate method to the Story class; doing so will compute the total number of stories in a phase and throw an exception if you exceed the limit. For now, hardcode the limits for each phase inside the Story class:

Here you’re selecting the number of stories in a given phase and checking with the hardcoded values in the phaseLimits. If the values match, you throw the validation exception. The compute(count) is a way to invoke a count function on the result of the where function. The compute Squeryl function is similar to the Squeryl select function except compute can invoke other aggregate functions like count. Check the Squeryl documentation[5] for all the available functions and operators.

5 “Group and Aggregate Queries,” http://squeryl.org/group-and-aggregate.html.

Now, to move a story to a different phase, add another method, called moveTo, to the Story class. This method will take the target phase and validate whether you’re crossing the limit. If everything is okay it will update the phase value of the story. This method will also return Either[Throwable, String] so that your Scalaz application class responds accordingly. To update a given row in the database, Squeryl provides an update method, which takes an instance of table (in this case it’s the stories object defined in the Schema class) and a function that will update the row. To update a story identified by this, you can use the Squeryl update function like this:

update(stories)(s =>
              where(s.number === this.number)
              set(s.phase := phase)
        )

Here, inside the function passed to update, you’re first identifying the story you need to update using the where method and then invoking the set method by passing the target phase value. Here’s the completed moveTo method defined in the Story class:

To invoke this moveTo method from the application class you have to first find the story object using the story number. To find a story by its number, add a method called findByNumber to your singleton Story object. This method will query the "STORIES" table and find the one that matches the given story number. Here’s how the method is implemented:

def findByNumber(number: String) =
tx { stories.where(s => s.number === number).single }

Here you’re using the single method defined in the Squeryl Query object that returns the first row from the query. At this point you’re all done with your model changes. Before you hook this new method to your application class, look at the following listing, which shows the complete implementation of the Story class.

Listing 7.11. Complete Story model implementation

Now the only thing remaining is to handle the Ajax call in your application class so you can update the database when the card is moved in the UI. You do this with an Ajax call using the moveCard function defined in the main.js file (see listing 7.10):

function moveCard(storyNumber, phase) {
  $.post("/card/move", {storyNumber: storyNumber, phase: phase},
     function(message) {
        $('#message').html(message)
       });
}

This method gets called when you drop a card in a phase from the drop method (see listing 7.8). This function makes an HTTP POST call to the /card/move URL by passing the storyNumber and the target phase of the story that moved—and handles the response by appending the message to the DOM element identified by the id message.

In the application class add a moveCard function to handle the /card/move POST request. This function first finds an instance of the Story class by its number and then calls the moveCard method you added to the Story class. Based on whether the move failed or succeeded, you’ll return a response message. Add the following moveCard method to the WeKanbanApplication class:

private def moveCard(implicit request: Request[Stream],
    servletRequest: HttpServletRequest) = {
    val number = param_!("storyNumber")
    val toPhase = param_!("phase")
    val story = Story.findByNumber(number)
    story.moveTo(toPhase) match {
      case Right(message) => OK(ContentType, "text/html") <<
             strict << message
      case Left(error) => OK(ContentType, "text/html") <<
             strict << error.getMessage
    }
}

The first thing you do in the moveCard method is retrieve both story number and phase from the Scalaz request. Here you’re using the param_! method already defined in the application class. Next, using the story number, you retrieve the Story model object associated with it and invoke the moveTo method to update the phase. Listing 7.11 shows how these methods are implemented inside the Story model class. Based on the response from the moveTo method, you show either a success message or an error message.

To invoke this moveCard method, let’s add another pattern matching case to the handle method to match the POST request to move a card:

case MethodParts(POST, "card" :: "move" :: Nil) =>
        Some(moveCard)

That’s it. Now if you run the weKanban application with all the changes, you’ll be able to move a card between phases, and your changes will persist. The following listing shows the complete WeKanbanApplication class.

Listing 7.12. Completed WeKanbanApplication class

At this point I think we’re done with the second story, and your application is ready to ship. You can use the package build action to create a WAR file deployable to any web container. You’ve implemented the most basic features of a web-based Kanban board. You can easily extend the application to make it more sophisticated.

7.3. Summary

In chapters 6 and 7 you took your first step in building medium-sized Scala applications. For the first time you moved outside the RPEL to build your Scala application. You used the SBT. You learned how to work with SBT, configure it, and manage dependencies—which is important when it comes to building enterprise-level Scala applications. You used Scalaz’s HTTP module to build your web application and also learned how functional programming concepts can be used in web applications. You saw how Scalaz uses Scala’s higher-order functions and pattern matching to expose nice APIs. Building enterprise-level applications most of the time means you have to work with relational databases. You looked into Squeryl, the Scala ORM tool, to understand how a relational database can be used and modeled in your Scala application.

This chapter has provided enough of a foundation to work with various Scala tools to build your next application. I encourage you to try different Scala ORM tools, view template engines, and web frameworks to build or extend this application. I hope the concepts and the tools you’ve learned in these two chapters will make you comfortable working with the various Scala tools available in the market.

In this chapter you got a glimpse of implicit conversion and implicit parameters. In the next chapter we’ll take a deep dive into the Scala type system and see how we can build abstraction layers using types.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset