Posts Tagged ‘Scala’

¡Lanzamiento de Play 2.0!

Traducido del blog de Typesafe

¡Presentando Play 2.0!

En noviembre del año pasado anunciábamos que Play, el framework de alta productividad para Java y Scala, pasaría a formar parte del stack de Typesafe. Hoy, apenas unos meses más tarde, nos complace poder anunciar que Play 2.0 ya está disponible como parte del Stack Open Source de Typesafe en su versión 2.0, junto con el soporte comercial de la subscripción de Typesafe.

Veamos algunas de sus principales características.

Soporte nativo para Java y Scala

Mientras que la versión original de Play Framework estaba escrita principalmente en Java, ofreciendo soporte para scala a través de plug-in, Play 2.0 adopta Scala de una manera más decidida. No sólo el núcleo central de Play ha sido migrado a Scala, sino que esta nueva versión brinda un soporte de primer nivel para el desarrollo de aplicaciones web con Scala. De manera tal que la nueva versión del framework provee dos API totalmente completas: una para desarrolladores de Scala y otra para desarrolladores Java.

Un controlador hecho con Java

El mismo controlador usando Scala

Desarrollo rápido de aplicaciones

Una de las características que hacían a la “experiencia de usuario” del desarrollador Play de la serie 1.x, era la consola de desarrollo y el reporte de errores en el propio explorador web. Play 2.0 mejora estas prestaciones al permitirle al desarrollador ejecutar porciones de código, pruebas o scripts de la línea de comando interactuando directamente con el runtime de la aplicación, y también compilando muchas de las partes típicas de una aplicación web.

Interactuando con los modelos de una aplicación Play 2.0 desde la consola

Llevando la seguridad de tipos a un nuevo nivel

Una de las ventajas de utilizar un lenguaje estáticamente tipado para escribir aplicaciones de Play, es que el compilador podía verificar partes del código por usted, Es por ello que Play 2.0 utiliza por defecto un lenguaje de templates basado en Scala, incluso para aquéllos desarrolladores que usen Java como su principal lenguaje de programación. Esto no signifca que usted deba convertirse en un experto en Scala para escribir templates en Play 2.0. Pero ahí está Scala, sin que usted lo note, trabajando en su beneficio.

Play 2.0 lleva las verificaciones en tiempo de compilación y el chequeo de tipos todavía más a fondo. Las rutas (que definen los URLs y el mapeo con las acciones), los templates, e incluso los recursos de la aplicación web son compilados (utilizando LESS, CoffeScript y el compilador de Google Clousure), poniendo a su disposición un entorno de desarrollo unificado tanto para los desarrolladores del lado del cliente como para los del lado del servidor. El resultado de todo esto es que más errores serán detectados en tiempo de compilación, acelerando el proceso de desarrollo de aplicaciones web. También hará muchísimo más simple trabajar en proyectos grandes que involucren numerosos desarrolladores.

Example route compilation failure detection

Jugando limpio

Play 1.x implementaba muchas de sus originales ideas (como la implementación automática de propiedades para Java y la recarga dinámica de sus clases) utilizando técnicas que requerían un runtime específico para Play. En cambio, Play 2.0 adopta un enfoque mucho más estándar para el despliegue del runtime. Esto fue posible escribiendo parte del framework en scala, y también aprovechando prestaciones de SBT, la popular herramienta de Scala para la construcción y despliegue de aplicaciones.

Play 2.0 mantiene la misma experiencia de los usuarios de la versión 1.x acostumbrados a ejecutar “play new, run start”, al mismo tiempo que parte de una base más fácil de extender. Play 2.0 ya trae configurado un script de compilación que funcionará sin modificaciones en la mayoría de los casos, pero si usted necesita personalizar la manera en que su aplicación es construida y desplegada, tendrá las herramientas a su alcance para adapatarla a sus necesidades. Como resultado de todo esto, hallará que será mucho más simple desplegar sus aplicaciones de Play 2.0 en los escenarios más variados.

Elija tan sólo las partes que va a usar

Dado que el diseño de aplicaciones web ha evolucionado en gran medida en los últimos años, Play 2.0 está construido siguiendo un criterio modular que le dará gran flexibilidad a la hora de elegir la tecnología a usar. ¿No desea usar una base de datos? Puede simplemente deshabilitar el plugin DB. ¿Quiere usar su propio motor de templates? Simplemente agrégue el plugin. ¿No necesita un framework web con todos los chiches? Use play como una simple librería. Usted decide qué tanto de la arquitectura de Play quiere usar en sus aplicaciones.

Deshabilitando plugins en el archivo conf/application.conf

Escale su apliación con Akka

Play está basado en una arquitectura liviana, sin estado, pensada para la web, que le asegura un consumo de recursos mínimo y predecible (en términos de CPU, memoria, threads) para desarrollar aplicaciones que requieren alta escalabidad. Esto es posible en parte gracias a Akka 2.0, el middleware basado en eventos que se encuentra en el corazón de Play 2.0. Los desarrolladores tienen acceso a toda la funcionalidad provista por Akka para construir aplicaciones altamente distribuidas que puedan ser escaladas para cumplir con los más altos niveles de demanda.

En este ejemplo puede ver como el resultado de un actor encargado de realizar un cálculo complejo, puede ser mapeado a un resultado de Play de una manera no-bloqueante

En este ejemplo puede ver como el actor ChatRoom envía un mensaje en porciones al cliente utilizando Comet

Manejo avanzado de entrada/salida y de Streams

Una de las más recientes tendencias en el desarrollo de aplicaciones web es el énfasis puesto en tecnologías de tipo push y no-bloqueantes. Play 2.0 utiliza una implementación de entrada/salida basada en Iteratee, que provee soporte para tecnologías avanzadas de push y streaming, partiendo desde WebSockets y Comet hasta streaming de archivos.

Un controlador de scala que utiliza el soporte para WebSockets de Play 2.0

Listos… preparados… a jugar con Play!

Play 2.0 es el resultado de una intensa colaboración entre Typesafe (liderados por Peter Hausel), el fundador del proyecto Guillaume Bort, la comunidad Play y Zenexity, la consultora orientada a la web donde Play fue creado. Ha sido una experiencia divertida y excitante trabajar junto con un grupo tan talentoso de desarrolladores.

Estamos sumamente complacidos de que Play hoy sea parte del Stack de Typesafe, junto con Scala, Akka y el soporte comercial, mantenimiento y herramientas de despliegue y administración (como la nueva consola de Typesafe) que juntas conforman el paquete de Subscripción de Typesafe. Typesafe también ofrece cursos de capacitación y consultoría para la nueva versión 2.0, para que su equipo pueda cuanto antes comenzar a desarrollar aplicaciones con Play 2.0.

Estamos convencido de que el lanzamiento de Play 2.0 marcará un antes y un después en las comunidades de Java y Scala, poniendo al alcance de los desarrolladores una experiencia novedosa que posibilitará inéditos niveles de escalabilidad y productividad.

Para que pueda empezar ya mismo con Play 2.0, hemos provisto varias aplicaciones de ejemplo con esta nueva versión. Así que lo invitamos a verlo en acción para decidir por usted mismo, ¡y divertirse haciéndolo!

Ya que ha llegado hasta aquí, no pierda los anuncios recientes de la versión 2.0 del Stack de TypesafeAkka 2.0, y la Consola de Typesafe.

Para más información, lean el anuncio original en el blog de Typesafe

Advertisements

Secure JSON Services with Play Scala and SecureSocial, by Matt Raible

Matt Raible has kindly agreed to let us publish his article on playlatam. You can find the original article at his site.

 

Last November, I traveled to Antwerp to speak at Devoxx. After my talk on HTML5 with Play Scala, Mattias Karlsson approached me and we had a chat about doing the same talk at Jfokus in Stockholm. I agreed and we began talking details after Trish and I returned to the US.

jFocus

I wrote this article on a plane between Denver and Seattle and will be hopping over the North Pole to Stockholm via Iceland tonight. For the past couple of weeks, I’ve been updating my Play More! HTML5/mobile app to add some new features. Most notably, I wanted to upgrade to Play 2.0, create JSON services and add authentication.

Upgrading to Play 2.0

My attempt to upgrade to Play 2.0 involved checking out the source from GitHub, building and installing the RC1 snapshot. As I tried to upgrade my app and started getting failed imports, I turned to the internet (specifically StackOverflow) to see if it was a good idea. The first answer for that question suggested I stay with 1.x.

If it’s a critical project, to be finished before next March 2012, I would go with Play 1.x. If it’s a less important project, which could be delayed, and that in any case won’t be released before March 2012, try Play 2.0.

While I didn’t plan on releasing Play More! before Jfokus, I decided upgrading didn’t add a whole lot to the talk. Also, I couldn’t find a Play Scala 0.9.1 to Play 2.0 upgrade guide and I didn’t have enough time to create one. So I decided to stick with Play 1.2.4 and add some JSON services for my iPhone client.

JSON Servers

I found Manuel Bernhardt’s Play! Scala and JSON. This led me to Jerkson, built by the now infamous@coda. I was able to easily get things working fairly quickly and wrote the following WorkoutService.scala:

package controllers.api

import play.mvc.Controller
import models._
import com.codahale.jerkson.Json._

object WorkoutService extends Controller {

  def workouts = {
    response.setContentTypeIfNotSet("application/json")
    generate(Workout.find().list())
  }
  def edit(id: Long) = {
    generate(Workout.byIdWithAthleteAndComments(id))
  }

  def create() = {
    var workout = params.get("workout", classOf[Workout])
    Workout.create(workout)
  }

  def save(id: Option[Long]) = {
    var workout = params.get("workout", classOf[Workout])
    Workout.update(workout)
  }

  def delete(id: Long) = {
    Workout.delete("id={id}").on("id" -> id).executeUpdate()
  }
}

Next, I added routes for my new API to conf/routes:

GET     /api/workouts               api.WorkoutService.workouts
GET     /api/workout/{id}           api.WorkoutService.edit
POST    /api/workout                api.WorkoutService.create
PUT     /api/workout/{id}           api.WorkoutService.save
DELETE  /api/workout/{id}           api.WorkoutService.delete

Then I created an ApiTest.scala class that verifies the first method works as expected.

import play.test.FunctionalTest
import play.test.FunctionalTest._
import org.junit._

class ApiTests extends FunctionalTest {
  
    @Test
    def testGetWorkouts() {
        var response = GET("/api/workouts");
        assertStatus(200, response);
        assertContentType("application/json", response)
        println(response.out)
    }
}

I ran “play test”, opened my browser to http://localhost:9000/@tests and clicked ApiTests -> Start to verify it worked. All the green made me happy.

Play More API Tests

Finally, I wrote some CoffeeScript and jQuery to allow users to delete workouts and make sure delete functionality worked.

$('#delete').click ->
  $.ajax
    type: 'POST'
    url: $(this).attr('rel')
    error: ->
      alert('Delete failed, please try again.')
    success: (data) ->
      location.href = "/more"

I was very impressed with how easy Play made it to create JSON services and I smiled as my CoffeeScript skills got a refresher.

The Friday before we left for Devoxx, I saw the module registration request for SecureSocial.

SecureSocial with Play Scala
From SecureSocial’s README:

SecureSocial allows you to add an authentication UI to your app that works with services based on OAuth1, OAuth2, OpenID and OpenID+OAuth hybrid protocols.

It also provides a Username and Password mechanism for users that do not wish to use existing accounts in other networks.

The following services are supported in this release:

  • Twitter (OAuth1)
  • Facebook (OAuth2)
  • Google (OpenID + OAuth Hybrid)
  • Yahoo (OpenID + OAuth Hybrid)
  • LinkedIn (OAuth1)
  • Foursquare (OAuth2)
  • MyOpenID (OpenID)
  • WordPress (OpenID)
  • Username and Password

In other words, it sounded like a dream come true and I resolved to try it once I found the time. That time found me last Monday evening and I sent a direct message to @jaliss (the module’s author) via Twitter.

Does Secure Social work with Play Scala? I’d like to use it in my Play More! project.

Jorge responded 16 minutes later saying that he hadn’t used Play Scala and he’d need to do some research. At 8 o’clock that night (1.5 hours after my original DM), Jorge had a sample working and emailed it to me. 10 minutes later I was adding a Secure trait to my project.

package controllers

import play.mvc._
import controllers.securesocial.SecureSocial

/*
 * @author Jorge Aliss <jaliss@gmail.com> of Secure Social fame.
 */
trait Secure {
  self: Controller =>

  @Before def checkAccess() {
    SecureSocial.DeadboltHelper.beforeRoleCheck()
  }

  def currentUser = {
    SecureSocial.getCurrentUser
  }
}

I configured Twitter and Username + Password as my providers by adding the following toconf/application.conf.

securesocial.providers=twitter,userpass

I also had to configure a number of securesocial.twitter.* properties. Next, I made sure my routes were aware of SecureSocial by adding the following to the top of conf/routes:

  *       /auth               module:securesocial

Then I specified it as a dependency in conf/dependencies.yml and ran “play deps”.

    - play -> securesocial 0.2.4

After adding “with Secure” to my Profile.scala controller, I tried to access its route and was prompted to login. Right off the bat, I was shown an error about a missing jQuery 1.5.2 file in my “javascripts” folder, so I added it and rejoiced when I was presented with a login screen. I had to add the app on Twitter to use its OAuth servers, but I was pumped when both username/password authentication worked (complete with signup!) as well as Twitter.

The only issue I ran into with SecureSocial was that it didn’t find the default implementation of SecureSocial’s UserService.Service when running in prod mode. I was able to workaround this by adding a SecureService.scala implementation to my project and coding it to talk to my Athlete model. I didn’t bother to hook in creating a new user when they logged in from Twitter, but that’s something I’ll want to do in the future. I was also pleased to find out customizing SecureSocial’s views was a breeze. I simply copied them from the module into my app’s views and voila!

package services

import play.db.anorm.NotAssigned
import play.libs.Codec
import collection.mutable.{SynchronizedMap, HashMap}
import models.Athlete
import securesocial.provider.{ProviderType, UserService, SocialUser, UserId}

class SecureService extends UserService.Service {
  val activations = new HashMap[String, SocialUser] with SynchronizedMap[String, SocialUser]

  def find(userId: UserId): SocialUser = {
    val user = Athlete.find("email={email}").on("email" -> userId.id).first()

    user match {
      case Some(user) => {
        val socialUser = new SocialUser
        socialUser.id = userId
        socialUser.displayName = user.firstName
        socialUser.email = user.email
        socialUser.isEmailVerified = true
        socialUser.password = user.password
        socialUser
      }
      case None => {
        if (!userId.provider.eq(ProviderType.userpass)) {
          var socialUser = new SocialUser
          socialUser.id = userId
          socialUser
        } else {
          null
        }
      }
    }
  }

  def save(user: SocialUser) {
    if (find(user.id) == null) {
      val firstName = user.displayName
      val lastName = user.displayName
      Athlete.create(Athlete(NotAssigned, user.email, user.password, firstName, lastName))
    }
  }

  def createActivation(user: SocialUser): String = {
    val uuid: String = Codec.UUID()
    activations.put(uuid, user)
    uuid
  }

  def activate(uuid: String): Boolean = {
    val user: SocialUser = activations.get(uuid).asInstanceOf[SocialUser]
    var result = false

    if (user != null) {
      user.isEmailVerified = true
      save(user)
      activations.remove(uuid)
      result = true
    }

    result
  }

  def deletePendingActivations() {
    activations.clear()
  }
}

Jorge was a great help in getting my authentication needs met and he even wrote a BasicAuth.scala trait to implement Basic Authentication on my JSON services.

package controllers

import _root_.securesocial.provider.{UserService, ProviderType, UserId}
import play._
import play.mvc._
import play.libs.Crypto

import controllers.securesocial.SecureSocial

/*
 * @author Jorge Aliss <jaliss@gmail.com> of Secure Social fame.
 */
trait BasicAuth {
  self: Controller =>

  @Before def checkAccess = {
    if (currentUser != null) {
      // this allows SecureSocial.getCurrentUser() to work.
      renderArgs.put("user", currentUser)
      Continue
    }

    val realm =
      Play.configuration.getProperty("securesocial.basicAuth.realm", "Unauthorized")

    if (request.user == null || request.password == null) {
      Unauthorized(realm)
    } else {
      val userId = new UserId
      userId.id = request.user
      userId.provider = ProviderType.userpass
      val user = UserService.find(userId)

      if (user == null ||
        !Crypto.passwordHash(request.password).equals(user.password)) {
        Unauthorized(realm)
      } else {
        // this allows SecureSocial.getCurrentUser() to work.
        renderArgs.put("user", user)
        Continue
      }
    }
  }

  def currentUser = {
    SecureSocial.getCurrentUser()
  }
}

Summary

My latest pass at developing with Scala and leveraging Play to build my app was a lot of fun. While there were issues with class reloading every-so-often and Scala versions with Scalate, I was able to add the features I wanted. I wasn’t able to upgrade to Play 2.0, but I didn’t try that hard and figured it’s best to wait until its upgrade guide has been published.

I’m excited to describe my latest experience to the developers at Jfokus this week. In addition, the conference has talks on Play 2.0CoffeeScriptHTML5Scala and Scalate. I hope to attend many of these and learn some new tricks to improve my skills and my app.

Update: The Delving developers have written an article on Migration to Play 2. While it doesn’t provide specific details on what they needed to change, it does have good information on how long it took and things to watch for.

Republished from Raible Designs. You can find the original article here.

First steps with Scala revisited: bash and python strikes back

I’ve received a good ammount of positive feedback on my previous article on scala.

A couple of readers prefered the bash one-liner version, and many of them argued that for such a simple task it was preferable a bash or python script. Luckily all of them understood that this was just a (maybe lousy, I admmit) excuse to give scala a try, and talk a little bit about functional programming, type inference, interacting with java, higher order functions, and, well, scala itself.

Nevertheless, to make justice to bash and scala, I took some advices from the discussion at hacer news, and even though I’m no bash nor python expert, with some googling around I managed to reproduce the funcionality of the scala script.

Bash eight-liner version

Well, here’s the bash version:

total_size=$(du --summarize *.textile --total | tail -n 1 | cut -f 1)
translated_files=$(grep -L "Esta página todavía no ha sido traducida al castellano" *.textile)
translated_size=$(echo $translated_files | tr '\n ' '\0' | xargs -0 du --summarize --total | tail -n 1 | cut -f 1)
translated_percent=$(($translated_size*100/$total_size))
echo "translated size: ${translated_size}kb/${total_size}kb ${translated_percent}% \
(pending $(($total_size-$translated_size))kb $((100-$translated_percent))%)"

total_count=$(ls *.textile | wc -l)
translated_count=$(echo $translated_files | tr ' ' '\n' | wc -l)
translated_percent=$(($translated_count*100/$total_count))
echo "translated files: ${translated_count}/${total_count} $(($translated_count*100/$total_count))% \
(pending $(($total_count-$translated_count)) $((100-$translated_percent))%)"

I just had to read a couple of man pages and struggle a little bit with tr, wc, xargs, tail, cut and that sort of stuff.

Python version

#! /usr/bin/env python
# -*- coding: utf-8 -*-

import fnmatch
import os

total_files = [file for file in os.listdir('.') if fnmatch.fnmatch(file, '*.textile')]
translated_files = [file for file in total_files if "Esta página todavía no ha sido traducida al castellano" not in open(file).read()]

total_size = sum([os.path.getsize(file) for file in total_files]) / 1000
translated_size = sum([os.path.getsize(file) for file in translated_files]) / 1000
translated_percent= translated_size * 100 / total_size

print "translated size: %dkb/%dkb %d%% (pending %dkb %d%%)" % \
      (translated_size, total_size, translated_percent, total_size-translated_size, 100-translated_percent)

total_count=len(total_files)
translated_count=len(translated_files)
translated_percent= translated_count * 100 / total_count

print "translated files: %d/%d %d%% (pending %d %d%%)" % \
      (translated_count, total_count, translated_percent, total_count-translated_count, 100-translated_percent)

What else can I say? The python version was really easy.

Scala, Bash and Python… FIGHT!

Well, now let’s see the output of each version:

sas@ubuntu:~/devel/apps/playdoces/documentation/1.2.4/manual$ ./status.scala 
translated size: 407kb/624kb 65% (pending 217kb 35%)
translated files: 37/64 57% (pending 27 43%)

sas@ubuntu:~/devel/apps/playdoces/documentation/1.2.4/manual$ ./status.sh 
translated size: 476kb/752KB 63% (pending 276kb 37%)
translated files: 37/64 57% (pending 27 43%)

sas@ubuntu:~/devel/apps/playdoces/documentation/1.2.4/manual$ ./status.py 
translated size: 407kb/624kb 65% (pending 217kb 35%)
translated files: 37/64 57% (pending 27 43%)

It seems like du rounds up the files size, but apart from that everything works as expected.

What about performance?

While the scala version do have a startup penalty, with the savecompiled option turned on, the delay is pretty bearable (without it the compiling process takes a little less than two seconds). Moreover, with long running or more complex tasks, I suspect that the benefits of having a compiled script, and the performance optimizations of the JVM, would certainly show up.

Here are some figures to compare.

sas@ubuntu:~/devel/apps/playdoces/documentation/1.2.4/manual$ time ./status.scala
translated size: 407kb/624kb 65% (pending 217kb 35%)
translated files: 37/64 57% (pending 27 43%)
real	0m0.475s
user	0m0.388s
sys	0m0.056s

sas@ubuntu:~/devel/apps/playdoces/documentation/1.2.4/manual$ time ./status.sh
translated size: 476kb/752KB 63% (pending 276kb 37%)
translated files: 37/64 57% (pending 27 43%)
real	0m0.045s
user	0m0.004s
sys	0m0.008s

sas@ubuntu:~/devel/apps/playdoces/documentation/1.2.4/manual$ time ./status.py
translated size: 407kb/624kb 65% (pending 217kb 35%)
translated files: 37/64 57% (pending 27 43%)
real	0m0.039s
user	0m0.020s
sys	0m0.012s

Conclusion

After playing a bit with all three of them, for this kind of tasks I’d definitely go with python. It’s really a joy to use, it’s got great documentation and there’s lot of interesting information at stack overflow. Moreover, like the scala version, and unlike bash, is portable across different platforms, I haven’t tried it but it should work just fine on windows.

Nevertheless I expect to keep playing with scala, for learning purposes and just to have some fun…

In the next article, I give scala another chance, and at the same time have a look at Implicit conversions, Scala’s answer to ruby’s open classes.

First steps with Scala, a functional alternative to bash scripts…

update: after getting some feedback I decided to make a bash and python version of this script. Check it out here.

another update: I revisited the scala version of the script, and at the same time talked a little bit about Implicit conversions, Scala’s answer to ruby’s open classes. Have a look at it here.

Those who know me are aware that I’ve been following play framework, and actively taking part of it’s community, for a couple of years.

Playframework 2.0 is right around the corner, and it’s core is programmed in Scala, so it’s a wonderful opportunity to give this object-oriented / functional hybrid beast a try…

Like many others, I will pick a very simple script to give my first steps…

Finding an excuse to give Scala a try

With a couple of friends we are on the way to translate play framework documentation to spanish (go have a look at it at http://playdoces.appspot.com/, by the way, you are more than welcome to collaborate with us)

The documentation is composed of a bunch of .textile files, and I had a very simple and silly bash script to track our advance. Every file that has not yet been translated has the phrase “todavía no ha sido traducida” in it’s first line

echo pending: `grep "todavía no ha sido traducida" * | wc -l` / `ls | wc -l`

Which produced something like

pending: 40 / 63

Pretty simple, right?

I just wanted to develop a simple scala script to count the translated files, and also it’s size, to know how much work we had ahead.

Scala as a scripting language

Using scala as a scripting language is pretty simple. Just enter some scala code in a text file, and execute it with “scala file.scala“. You can also try it with the interactive interpreter, better knonw as REPL (well, it’s not really an interpreter, but a Read-Evaluate-Print Loop, that’s where the REPL name comes from).

In linux, you can also excute them directly from the shell marking the scala file as executable and adding these lines to the beginning of the file.

#!/bin/sh
exec scala "$0" "$@"
!#

Tip: you can speed up A LOT script execution by adding a -savecompiled like it says on the scala command man page, like this:

#!/bin/sh
exec scala -savecompiled "$0" "$@"
!#

Classes and type inference in scala

So I created a DocumentationFile, with a name, length and an isTranslated property.

class DocumentationFile(val file: File) {

  val name = file.getName
  val length = file.length
  val isTranslated = (firstLine.indexOf("Esta página todavía no ha sido traducida al castellano") == -1)

  def firstLine = new BufferedReader(new FileReader(file)).readLine

}

Scala takes away a lot of boilerplate code. The constructor is right there, along with the class declaration. In our case, the DocumentationFile constructor takes a java.io.File as argument.

Scala also makes heavy use of type inference to alleviate us from having to declare every variable’s type. That’s why you don’t have to specify that name is a String, length a Long and isTranslated a Boolean. You still have to declare types on method’s arguments, but usually you can omit them everywhere else.

Working with collections

Next I needed to get all textile files from the current directory, instantiate a DocumentationFile for each of them, and save them in an Array for later processing.

import java.io._

val docs = new File(".").listFiles
  .filter(_.getName.endsWith(".textile"))   // process only textile files
  .map(new DocumentationFile(_))

Technically speaking is just one line of code. The “_” is just syntactic sugar, we could have written it in a more verbose way like this:

val docs = new File(".").listFiles
  .filter( file => file.getName.endsWith(".textile") )   // process only textile files
  .map( file => new DocumentationFile(file) )

Or if you are a curly braces fun:

val docs = new File(".").listFiles
  .filter { file => 
    file.getName.endsWith(".textile")         // process only textile files
  }   
  .map { file => 
    new DocumentationFile(file)
  }

Higher order functions

Once we have all textile files, we’ll need the translated ones.

val translated = docs.filter(_.isTranslated)

Here we are passing the filter method a function as parameter (that’s what is called a higher order function). That function is evaluated for every item in the Array, and if it returns true, that item is added to the resulting Array. The “_.isTranslated” stuff is once again just syntactic sugar. We could have also written the function as follows:

val translated = docs.filter( (doc: DocumentationFile) => doc.isTranslated )

Functional versus imperative: To var or not to var

Now I need to calculate the quantity and size of the translated and not yet translated files. Counting the files is pretty easy, just have to use “translated.length” to know how many files have been translated so far. But for counting their size I have to sum the size of each one of them.

This was my first attempt:

var translatedLength = 0L
translated.foreach( translatedLength += _.length ) 

In scala we can declare variables with the “var” and “val” keywords, the first ones are mutable, while the later one ar immutables. Mutable variables are read-write, while immutable variables can’t be reassigned once their value has been established (think of them like final variables in Java).

While scala allows you to work in an imperative or functional style, it really encourages the later one. Programming in scala, kind of the scala bible, even teaches how to refactor your code to avoid the use of mutable variables, and get your head used to a more functional programming style.

These are several ways I’ve found to calculate it in a more functional style (thanks to stack overflow!)

val translatedLength: Long = translated.fold(0L)( (acum: Long, element: DocumentFile) => acum + element.length )

//type inference to the rescue
val translatedLength = translated.foldLeft(0L)( (acum, element) => acum + element.length )

//syntactic sugar
val translatedLength = translated.foldLeft(0L)( _ + _.length )

// yes, if statement is also an expression, just like the a ? b : c java operator.
val translatedLength = if (translated.length == 0) 0 else translated.map(_.length).sum

I’ve finally settled with this simple and short form:

val translatedLength = translated.map(_.length).sum
val docsLength = docs.map(_.length).sum

Default parameters and passing functions as arguments

Now I have all the information I needed, so I just have to show it on screen. I also wanted to show the file size in kbs.

Once again this was my first attempt:

println( 
  "translated size: " + asKB(translatedLength) + "/" + asKB(docsLength) + " " + 
  translatedLength * 100 / docsLength + "% "
)

println( 
  "translated files: " + translated.length + "/" + docs.length + " " + 
  translated.length * 100 / docs.length + "% "
)

def asKB(length: Long) = (length / 1000) + "kb"

And this was the output:

translated size: 256kb/612kb 41% 
translated files: 24/64 37% 

Well, it worked, but it could definitely be improved, there was too much code duplication.

So I created a function that took care of it all:

def status(
  title: String = "status", 
  current: Long, total: Long, 
  format: (Long) => String = (x) => x.toString): String = {

  val percent = current * 100 / total

  title + ": " + format(current) + "/" + format(total) + " " +
  percent + "%" +
  " (pending " + format(total - current) + " " +
  (100-percent) + "%)"
}

The only tricky part is the format parameter. It’s just a higher order function, that by default just converts the passed number to a String.

We use that function like this:

println( 
  status("translated size", translatedLength, docsLength, (length) => asKB(length) ) 
)

println( 
  status("translated files", translated.length, docs.length) 
)

And that’s it.

It’s really easy to achieve this kind of stuff using scala as a scripting language, and on the way you may learn a couple of interesting concepts, and give your first steps into functional programming.

In the next article, I have a look at a bash and python version of this script, to compare the scripting capabilities of each of them.

This is the complete script, here you have a github gist and you can also find it in the play spanish documentation project.

#!/bin/sh
exec scala -savecompiled "$0" "$@"
!#

import java.io._

val docs = new File(".").listFiles
  .filter(_.getName.endsWith(".textile"))   // process only textile files
  .map(new DocumentationFile(_))

val translated = docs.filter(_.isTranslated)    // only already translated files

val translatedLength = translated.map(_.length).sum
val docsLength = docs.map(_.length).sum

println( 
  status("translated size", translatedLength, docsLength, (length) => asKB(length) ) 
)

println( 
  status("translated files", translated.length, docs.length) 
)

def status(
  title: String = "status", 
  current: Long, total: Long, 
  format: (Long) => String = (x) => x.toString): String = {

  val percent = current * 100 / total

  title + ": " + format(current) + "/" + format(total) + " " +
  percent + "%" +
  " (pending " + format(total - current) + " " +
  (100-percent) + "%)"
}

def asKB(length: Long) = (length / 1000) + "kb"

class DocumentationFile(val file: File) {

  val name = file.getName
  val length = file.length
  val isTranslated = (firstLine.indexOf("Esta página todavía no ha sido traducida al castellano") == -1)

  override def toString = "name: " + name + ", length: " + length + ", isTranslated: " + isTranslated

  def firstLine = new BufferedReader(new FileReader(file)).readLine

}

updates:

I added some tips and advices I got from all the positive feedback to this article:

    2012-01-13 changed title, from “First steps with Scala, say goodbye to bash scripts…” to the current one, it’s less polemic, but more accurate.
%d bloggers like this: