TinyWeb in Scala

Let’s take TinyWeb and transform it into Scala. We’ll do this a bit at a time so we can show how our Scala code can work with the existing Java code. The overall shape of the framework will be similar to the Java version, but we’ll take advantage of some of Scala’s functional features to make the code more concise.

Step One: Changing Views

We’ll start with our view code. In Java, we used the classic Strategy pattern. In Scala, we’ll stick with the Strategy pattern, but we’ll use higher-order functions for our strategy implementations. We’ll also see some of the benefits of expressions over statements for control flow.

The biggest change we’ll make is to the view-rendering code. Instead of using Functional Interface in the form of RenderingStrategy, we’ll use a higher-order function. We go over this replacement in great detail in Pattern 1, Replacing Functional Interface.

Here’s our modified view code in its full functional glory:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/tinyweb/stepone/View.scala
 
package​ com.mblinn.mbfpp.oo.tinyweb.stepone
 
import​ com.mblinn.oo.tinyweb.RenderingException
 
 
trait​ View {
 
def​ render(model: Map[​String​, ​List​[​String​]]): ​String
 
}
 
class​ FunctionView(viewRenderer: (Map[​String​, ​List​[​String​]]) => ​String​)
 
extends​ View {
 
def​ render(model: Map[​String​, ​List​[​String​]]) =
 
try
 
viewRenderer(model)
 
catch​ {
 
case​ e: Exception => ​throw​ ​new​ RenderingException(e)
 
}
 
}

We start off with our View trait. It defines a single method, render, which takes a map representing the data in our model and returns a rendered String.

 
trait​ View {
 
def​ render(model: Map[​String​, ​List​[​String​]]): ​String
 
}

Next up, let’s take a look at the body of FunctionView. The code below declares a class that has a constructor with a single argument, viewRenderer, which sets an immutable field of the same name.

 
class​ FunctionView(viewRenderer: (Map[​String​, ​List​[​String​]]) => ​String​)
 
extends​ View {
 
classBody
 
}

The viewRenderer function parameter has a rather strange-looking type annotation, (Map[String, String]) => String. This is a function type. It says that viewRenderer is a function that takes a Map[String, String] and returns a String, just like the renderView on our Java RenderingStrategy.

Next, let’s take a look at the render method itself. As we can see from the code below, it takes in a model and runs it through the viewRender function.

 
def​ render(model: Map[​String​, ​List​[​String​]]) =
 
try
 
viewRenderer(model)
 
catch​ {
 
case​ e: Exception => ​throw​ ​new​ RenderingException(e)
 
}

Notice how there’s no return keyword anywhere in this code snippet? This illustrates another important aspect of functional programming. In the functional world, we program primarily with expressions. The value of a function is just the value of the last expression in it.

In this example, that expression happens to be a try block. If no exception is thrown, the try block takes on the value of its main branch; otherwise it takes on the value of the appropriate case clause in the catch branch.

If we wanted to supply a default value rather than wrap the exception up into a RenderException, we can do so just by having the appropriate case branch take on our default, as illustrated in the following code:

 
try
 
viewRenderer(model)
 
catch​ {
 
case​ e: Exception => ​""
 
}

Now when an exception is caught, the try block takes on the value of the empty string.

Step Two: A Controller First Cut

Now let’s take a look at transforming our controller code into Scala. In Java we used the Controller interface and the TemplateController class. Individual controllers were implemented by subclassing TemplateController.

In Scala, we rely on function composition just like we did with our views by passing in a doRequest function when we create a Controller:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/tinyweb/steptwo/Controller.scala
 
package​ com.mblinn.mbfpp.oo.tinyweb.steptwo
 
 
import​ com.mblinn.oo.tinyweb.HttpRequest
 
import​ com.mblinn.oo.tinyweb.HttpResponse
 
import​ com.mblinn.oo.tinyweb.ControllerException
 
import​ com.mblinn.oo.tinyweb.RenderingException
 
 
trait​ Controller {
 
def​ handleRequest(httpRequest: HttpRequest): HttpResponse
 
}
 
 
class​ FunctionController(view: View, doRequest: (HttpRequest) =>
 
Map[​String​, ​List​[​String​]] ) ​extends​ Controller {
 
 
def​ handleRequest(request: HttpRequest): HttpResponse = {
 
var​ responseCode = 200;
 
var​ responseBody = ​""​;
 
 
try​ {
 
val​ model = doRequest(request)
 
responseBody = view.render(model)
 
} ​catch​ {
 
case​ e: ControllerException =>
 
responseCode = e.getStatusCode()
 
case​ e: RenderingException =>
 
responseCode = 500
 
responseBody = ​"Exception while rendering."
 
case​ e: Exception =>
 
responseCode = 500
 
}
 
 
HttpResponse.Builder.newBuilder()
 
.body(responseBody).responseCode(responseCode).build()
 
}
 
}

This code should look fairly similar to our view code. This is a fairly literal translation of Java into Scala, but it’s not terribly functional because we’re using the try-catch as a statement to set the values of responseCode and responseBody.

We’re also reusing our Java HttpRequest and HttpResponse. Scala provides a more concise way to create these data-carrying classes, called case classes. Using the try-catch as an expression, as well as using case classes, can help cut down on our code significantly.

We’ll make both of these changes in our next transformation.

Immutable HttpRequest and HttpResponse

Let’s start by switching over to case classes instead of using the Builder pattern. It’s as simple as the code below:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/tinyweb/stepthree/HttpData.scala
 
package​ com.mblinn.mbfpp.oo.tinyweb.stepthree
 
 
case​ ​class​ HttpRequest(headers: Map[​String​, ​String​], body: ​String​, path: ​String​)
 
case​ ​class​ HttpResponse(body: ​String​, responseCode: ​Integer​)

We can create new HttpRequest and HttpResponse objects easily, as the following REPL output shows:

 
scala>​ val request = HttpRequest(Map("X-Test" -> "Value"), "requestBody", "/test")
 
request: com.mblinn.mbfpp.oo.tinyweb.stepfour.HttpRequest =
 
HttpRequest(Map(X-Test -> Value),requestBody,/test)
 
 
scala>​ val response = HttpResponse("requestBody", 200)
 
response: com.mblinn.mbfpp.oo.tinyweb.stepfour.HttpResponse =
 
HttpResponse(requestBody,200)

At first glance, this might seem similar to using a Java class with constructor arguments, except that we don’t need to use the new keyword. However, in Pattern 4, Replacing Builder for Immutable Object, we dig deeper and see how Scala’s ability to provide default arguments in a constructor, the natural immutability of case classes, and the ability to easily create a new instance of a case class from an existing instance lets them satisfy the intent of the Builder pattern.

Let’s take a look at our second change. Since a try-catch block in Scala has a value, we can use it as an expression rather than as a statement. This might seem a bit odd at first, but the upshot is that we can use the fact that Scala’s try-catch is an expression to simply have the try-catch block take on the value of the HttpResponse we’re returning. The code to do so is below:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/tinyweb/stepthree/Controller.scala
 
package​ com.mblinn.mbfpp.oo.tinyweb.stepthree
 
import​ com.mblinn.oo.tinyweb.ControllerException
 
import​ com.mblinn.oo.tinyweb.RenderingException
 
 
trait​ Controller {
 
def​ handleRequest(httpRequest: HttpRequest): HttpResponse
 
}
 
class​ FunctionController(view: View, doRequest: (HttpRequest) =>
 
Map[​String​, ​List​[​String​]] ) ​extends​ Controller {
 
def​ handleRequest(request: HttpRequest): HttpResponse =
 
try​ {
 
val​ model = doRequest(request)
 
val​ responseBody = view.render(model)
 
HttpResponse(responseBody, 200)
 
} ​catch​ {
 
case​ e: ControllerException =>
 
HttpResponse(​""​, e.getStatusCode)
 
case​ e: RenderingException =>
 
HttpResponse(​"Exception while rendering."​, 500)
 
case​ e: Exception =>
 
HttpResponse(​""​, 500)
 
}
 
}

This style of programming has a couple of benefits. First, we’ve eliminated a couple of extraneous variables, responseCode and responseBody. Second, we’ve reduced the number of lines of code a programmer needs to scan to understand which HttpResponse we’re returning from the entire method to a single line.

Rather than tracing the values of responseCode and responseBody from the top of the method through the try block and finally into the HttpResponse, we only need to look at the appropriate piece of the try block to understand the final value of the HttpResponse. These changes combine to give us code that’s more readable and concise.

Tying It Together

Now let’s add in the class that ties it all together, TinyWeb. Like its Java counterpart, TinyWeb is instantiated with a map of Controllers and a map of filters. Unlike Java, we don’t define a class for filter; we simply use a list of higher-order functions!

Also like the Java version, the Scala TinyWeb has a single method, handleRequest, which takes in an HttpRequest. Instead of returning an HttpResponse directly, we return an Option[HttpResponse], which gives us a clean way of handling the case when we can’t find a controller for a particular request. The code for the Scala TinyWeb is below:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/tinyweb/stepfour/Tinyweb.scala
 
package​ com.mblinn.mbfpp.oo.tinyweb.stepfour
 
class​ TinyWeb(controllers: Map[​String​, Controller],
 
filters: ​List​[(HttpRequest) => HttpRequest]) {
 
 
def​ handleRequest(httpRequest: HttpRequest): ​Option​[HttpResponse] = {
 
val​ composedFilter = filters.reverse.reduceLeft(
 
(composed, next) => composed compose next)
 
val​ filteredRequest = composedFilter(httpRequest)
 
val​ controllerOption = controllers.get(filteredRequest.path)
 
controllerOption map { controller => controller.handleRequest(filteredRequest) }
 
}
 
}

Let’s take a look at it in greater detail starting with the class definition.

 
class​ TinyWeb(controllers: Map[​String​, Controller],
 
filters: ​List​[(HttpRequest) => HttpRequest]) {
 
classBody
 
}

Here we’re defining a class that takes two constructor arguments, a map of controllers and a list of filters. Note the type of the filters argument, List[(HttpRequest) => HttpRequest]. This says that filters is a list of functions from HttpRequest to HttpRequest.

Next up, let’s look at the signature of the handleRequest method:

 
def​ handleRequest(httpRequest: HttpRequest): ​Option​[HttpResponse] = {
 
functionBody
 
}

As advertised, we’re returning an Option[HttpResponse] instead of an HttpResponse. The Option type is a container type with two subtypes, Some and None. If we’ve got a value to store in it, we can store it in an instance of Some; otherwise we use None to indicate that we’ve got no real value. We’ll cover Option in greater detail in Pattern 8, Replacing Null Object.

Now that we’ve seen the TinyWeb framework, let’s take a look at it in action. We’ll use the same example from the Java section, returning a list of friendly greetings. However, since it’s Scala, we can poke at our example in the REPL as we go. Let’s get started with our view code.

Using Scala TinyWeb

Let’s take a look at using our Scala TinyWeb framework.

We’ll start by creating a FunctionView and the rendering function we’ll compose into it. The following code creates this function, which we’ll name greetingViewRenderer, and the FunctionView that goes along with it:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/tinyweb/example/Example.scala
 
def​ greetingViewRenderer(model: Map[​String​, ​List​[​String​]]) =
 
"<h1>Friendly Greetings:%s"​.format(
 
model
 
getOrElse(​"greetings"​, ​List​[​String​]())
 
map(renderGreeting)
 
mkString ​", "​)
 
 
private​ ​def​ renderGreeting(greeting: ​String​) =
 
"<h2>%s</h2>"​.format(greeting)
 
 
def​ greetingView = ​new​ FunctionView(greetingViewRenderer)

We’re using a couple of new bits of Scala here. First, we introduce the map method, which lets us map a function over all the elements in a sequence and returns a new sequence. Second, we’re using a bit of syntactic sugar that Scala provides that allows us to treat any method with a single argument as an infix operator. The object on the left side of the operator is treated as the receiver of the method call, and the object on the right is the argument.

This bit of syntax means that we can omit the familiar dot syntax when working in Scala. For instance, the two usages of map below are equivalent:

 
scala>​ val greetings = List("Hi!", "Hola", "Aloha")
 
greetings: List[java.lang.String]
 
 
scala>​ greetings.map(renderGreeting)
 
res0: List[String] = List(<h2>Hi!</h2>, <h2>Hola</h2>, <h2>Aloha</h2>)
 
 
scala>​ greetings map renderGreeting
 
res1: List[String] = List(<h2>Hi!</h2>, <h2>Hola</h2>, <h2>Aloha</h2>)

Now let’s take a look at our controller code. Here we create the handleGreetingRequest function to pass into our Controller. As a helper, we use makeGreeting, which takes in a name and generates a random friendly greeting.

Inside of handleGreetingRequest we create a list of names by splitting the request body, which returns an array like in Java, converting that array into a Scala list and mapping the makeGreeting method over it. We then use that list as the value for the "greetings" key in our model map:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/tinyweb/example/Example.scala
 
def​ handleGreetingRequest(request: HttpRequest) =
 
Map(​"greetings"​ -> request.body.split(​","​).toList.map(makeGreeting))
 
 
private​ ​def​ random = ​new​ Random()
 
private​ ​def​ greetings = Vector(​"Hello"​, ​"Greetings"​, ​"Salutations"​, ​"Hola"​)
 
private​ ​def​ makeGreeting(name: ​String​) =
 
"%s, %s"​.format(greetings(random.nextInt(greetings.size)), name)
 
 
def​ greetingController = ​new​ FunctionController(greetingView, handleGreetingRequest)

Finally, let’s take a look at our logging filter. This function simply writes the path that it finds in the passed-in HttpRequest to the console and then returns the path unmodified:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/tinyweb/example/Example.scala
 
private​ ​def​ loggingFilter(request: HttpRequest) = {
 
println(​"In Logging Filter - request for path: %s"​.format(request.path))
 
request
 
}

To finish up the example, we need to create an instance of TinyWeb with the controller, the view, and the filter we defined earlier, and we need to create a test HttpResponse:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/tinyweb/example/Example.scala
 
def​ tinyweb = ​new​ TinyWeb(
 
Map(​"/greeting"​ -> greetingController),
 
List​(loggingFilter))
 
def​ testHttpRequest = HttpRequest(
 
body=​"Mike,Joe,John,Steve"​,
 
path=​"/greeting"​)

We can now run the test request through TinyWeb’s handleRequest method in the REPL and view the corresponding HttpResponse:

 
scala>​ tinyweb.handleRequest(testHttpRequest)
 
In Logging Filter - request for path: /greeting
 
res0: Option[com.mblinn.mbfpp.oo.tinyweb.stepfour.HttpResponse] =
 
Some(HttpResponse(<h1>Friendly Greetings:<h2>Mike</h2>, <h2>Nam</h2>, <h2>John</h2>,
 
200))

That wraps up our Scala version of TinyWeb. We’ve made a few changes to the style that we used in our Java version. First, we replaced most of our iterative code with code that’s more declarative. Second, we’ve replaced our bulky builders with Scala’s case classes, which give us a built-in way to handle immutable data. Finally, we’ve replaced our use of Functional Interface with plain old functions.

Taken together, these small changes save us quite a bit of code and give us a solution that’s shorter and easier to read. Next up, we’ll take a look at TinyWeb in Clojure.

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

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