Migrating from RubyOnRails to Scala Play 2.0 (part 1)

The last week I was on vacation and I used the time to study a new technology (things that nowadays I don’t have time to do in daily life).

I decided to migrate my wife’s website (for those who don’t know, my wife is the very famous brazilian singer Daniella Alcarpe, known also as @cantora).

The website is quite simple and easy to develop. Actually, there is no rocket science at all in everything I did. But I had a lot of fun and I learn a lot of things. This post cannot be used as a complete migration plan for those who want to migrate from Rails to Play, but it will give you an idea of the work you will have. Basically, you will have to re-write everything from scratch… :-) or almost this…

First steps

The first thing I did was to visit the Play 2.0 website and follow the First Application Tutorial. I did both the Scala and the Java Tutorial. The tutorial is very easy to follow. It is well written, no big deal. One important thing to say is that I already studied Scala for six month last year, in my PhD class, so I was confortable with Scala. For those who never saw Scala, I suggest to read the book Programming in Scala 2nd Edition. I decided than to create the @cantora’s website in Scala, because it is a language that I love. It is very fun to write code in Scala!

I also decided to open the source of the @cantora’s website, so people can follow my steps (and maybe help to improve the site, or use it for other artists website).

I created a play project using the command

$ play new cantora

Then, I started to migrate the file app/views/layouts/application.html.erb in the old Rails application to the app/views/main.scala.html in the newly created Play 2.0 one.

The website has a dynamic title, which is set up using content_for block.

Rails code:

<title><%= t("site.title") %> - <%= yield(:title) %></title>

Scala Code:

<title>@Messages("site.title") - @title</title>

Note that the title is now a variable that I will pass to the layout, so my main.scala.html first line is:

@(title: String)(content: Html)

I also had to change every t(“key”) for @Messages(“key”). Off course I used a regular expression replace to do it in all files.

Stylesheets and Javascripts, which usually are a one line of code in Rails required a more “hardcoded” solution in Scala.

Rails code:

<%= javascript_include_tag :all, :cache => true %> <%= stylesheet_link_tag :all, :cache => true %>

Scala code:

<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/grid.css")">
<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/style.css")">
<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/galleriffic.css")">
<script src="@routes.Assets.at("javascripts/applicaton.js")" type="text/javascript"></script>
<script src="@routes.Assets.at("javascripts/jquery-galletiffic.js")" type="text/javascript"></script>
<script src="@routes.Assets.at("javascripts/jquery.js")" type="text/javascript"></script>
<script src="@routes.Assets.at("javascripts/jquery.youtubeplaylist.js")" type="text/javascript"></script>

Then I link in the main menu I had to replace for the Play 2.0 routes system:

Rails Code

<li class="menu"><%= link_to "#{t('site.singer')}", {:controller => "cantora"} %></li>

Scala Code

<li class="menu"><a href="@routes.Application.cantora">@Messages("site.singer")</a></li>

For this work, I had to add every route in the Play routes file. Different from Rails, that assumes your route based on controller and method name, you have to explicitly set the routes for every controller method. Actually I think this is not so bad. And if you want, you can easily create this “convention over configuration” scheme in Scala. So I added to the routes file:

GET     /cantora                           controllers.Application.cantora
GET     /musicas                           controllers.Application.music
GET     /agenda                           controllers.Application.shows
GET     /novidades                           controllers.Application.news
...

I have created every method in the default Application Controller:

def cantora = Action { implicit request =>; Ok(views.html.cantora()) }
def music = Action { implicit request =>; Ok(views.html.music()) }
def shows = Action { implicit request => Ok(views.html.shows()) }
def news = Action { implicit request => Ok(views.html.news()) }

And also a view file for every action I had. Then, app/views/[controller_name]/index.erb.html became app/views/[method_name].scala.html

Most of my controllers had just one default index action. Those who had more than the default index method, I choosed another action name for it and used the same pattern.

Calling web services from a Play 2.0 application

The @cantora’s website looks very simple, but there are some very interesting integrations in the back-end of it, which turns it into a very interesting website for the artist:

  1. All videos in the videos session are automatically imported from the artist channel
  2. All photos in the photos session are automatically imported from the photos posted in the Facebook Artist Fan Page
  3. The tweets in the social page are imported from the artist timeline
  4. Shows are an embedded Google Agenda
  5. News are from the artist Fan Page timeline
  6. Every time someone register for music download, this record goes automatically to the artist email marketing database (integrated with Mailee API)

Off course I needed to maintain all this features in the Play 2.0 version. The features 4 and 5 are trivial, since no back-end code is necessary, all code is HTML and javascript, the same in both Rails and Play version of the website

But for features 1, 2, 3 and 6, I had to re-write the code, which is pretty simple, except for the Mailee API integration, which gave me some extra effort.

For the videos (1)

Rails Controller Code:

require 'restclient'

class VideosController < ApplicationController
  caches_action :index

  def index
    uri = "http://gdata.youtube.com/feeds/api/users/daniellaalcarpe/uploads"
    video_feed = RestClient.get(uri)
    @videos = Hash.from_xml(video_feed)['feed']['entry']
  end
end

Scala Controller Code:

def videos = Cached("videos", 18000) {
    Action { implicit request =>
      Async {
        val uri = "http://gdata.youtube.com/feeds/api/users/daniellaalcarpe/uploads"
        WS.url(uri).get().map { response =>
          Ok(views.html.videos(response.xml \ "entry"))
        }
      }
    }
  }

And the views are also a little bit different:

Rails View Code:

  <% @videos.each do |video| %>
    <li>
       <a href="<%= video['group']['player']['url'] %>">
           <%= video['title'] %>
       </a>
    </li>
  <% end %>

Scala View Code:

@videos.map { video => 
  <li>
    <a href="@{ ((video \\ "group") \\ "player")(0).attribute("url")}">
       @{(video \ "title")(0).text}
    </a>
  </li>
}

Notice that XML manipulation in Scala is a little bit more tricky, but nothing that can harm you so deep.
The twitter feed XML for the social page is very similar to the video page code. The photos is a little bit different, because I need to make to Web Service requests: (1) to the the albuns and (2) to get the photos for each album:

Rails Controller Code:

require 'restclient'
class FotosController < ApplicationController
  caches_action :index, :expires_in => 1.day

  def index
    @albuns_photos = {}
    @albuns = []
    albuns = JSON.parse(RestClient.get("https://graph.facebook.com/daniella.alcarpe/albums"))["data"]
    albuns.each do |album|
      if (album['description'] == "*")
        photos = JSON.parse(RestClient.get("https://graph.facebook.com/#{album['id']}/photos"))["data"]
        albuns_photos = photos.map {|p| p["images"][3]["source"]}
        album['photos'] = albuns_photos
        @albuns << album
      end
    end
  end
end

Scala Controller Code:

  
def photos = Cached("photos", 18000) {
    Action { implicit request =>
      Async {
        WS.url("https://graph.facebook.com/daniella.alcarpe/albums").get().map { response =>
          val albuns = (response.json \ "data").
            as[List[JsObject]].filter(album =>
              (album \ "description").toString.equals("\"*\""))

          val photos = albuns.map { album =>
            WS.url("https://graph.facebook.com/" + (album \ "id").toString.replace("\"", "") + "/photos").get().map { response2 =>
              (response2.json \ "data").as[List[JsObject]].map { photo =>
                ((photo \ "images")(3) \ "source").toString.replace("\"", "")
              }
            }
          }

          Ok(views.html.photos(photos))
        }
      }
    }
  }

Again, manipulating JSON in Scala is more ugly, and I had to make a small hack to replace quotes in json for empty string. I don’t know why Play returns the strings with the quotes. Something to improve in the future.

As I said, the music download page is a little bit more tricky, so I’ll leave this code for a next post. I also created automated tests for the website, and I’ll show them in the next post also.

Conclusions

  • It took me 3 days to migrate everything from Rails to Play 2.0
  • Scala is a very nice language and the Play 2.0 framework s very mature and stable. The advantage in using Play is that it is very fast and with incredible performance. It runs inside the JVM. It is a stateless web solution. It seems perfect to use it for REST APIs, since the code remais as elegant as a ruby code, with the advantages of static typing languages.
  • It is very easy to deploy Play 2.0 applications. It comes with sbt, which is a very modern built tool. With play dist command, you create a package with everything you need to deploy the stand-alone application
  • Testing is similar to ruby specs. It also has continuous testing
  • The Play 2.0 documentation is very complete. Sometimes, for more difficult things, you will need to go to stackoverflow or the Play API. I did not get in much trouble to find documentation about the challenges I had
  • The only problem I had was with i18n. It looks like version 2.0 is not completely prepared for multiple languages websites. I found a bug report and fix that is already applied for version 2.1, but I did not tested it yet.

2 Comments Migrating from RubyOnRails to Scala Play 2.0 (part 1)

  1. Pingback: Rails vs Play Framework - AgileAndArt

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>