May 21, 2012

Step by step migrate your Groovy template to Rythm template - Part One

The content of this blog applies to PlayFramework 1.2.x

I've just release Rythm-1.0RC4 with new features including precompile support, null-safe expression and template class properties enhancement, I feel it's time to write this post as there is nearly nothing you can do with Groovy but not with Rythm. Yes you can leverage all your experiences gained from Play's Groovy template engine and apply them immediately to Rythm and gain a 3 to 20 times faster rendering speed with even more clean Razor like syntax.

So first of all, you are safe to do the migration, meaning you keep your system working at all time through out the whole process. Thanks to PlayFramework's rendering process and the plugin architecture, Rythm could implement an unobtrusive template engine plugin to playframework that co-exists with other template engines following the same convention.

To start using Rythm you need to add the following statement into your conf/dependencies.yml file:

    - play -> rythm 1.0.0-RC4 # 1.0.0-RC4 is the current version but you might change it accordingly

And then you go back to your console and type

play deps --sync
mkdir app/rythm

Congratulation! you got Rythm installed and rythm view root folder created, and it's time to take off. And here is what's going to be happened for each of your template files under app/views folder:

step1. copy the file to corresponding folder under app/rythm, e.g.
app/views/main.html -> app/rythm/main.html
app/views/Application/index.html -> app/rythm/Application/index.html

step2. update the file with Rythm syntax. (No worries, we will come back to this point very soon, I promise!)

step3. Type play run and go to your browser press F5 to check the result. You might rewind back to step2 if it needs to adjust here and there, but trust me it's not difficult to do. Once you are okay with this template, move to the next template file and repeat step 1, 2 and 3.

Remember that all the rest part of your entire application works except the template file you are updating. And by the end of each step 3, your whole application works like before. So don't be scared, it's not a big bung migration.

Now let's take our magnifier to inspect what is happening in step 2: update the groovy file with Rythm syntax.

1. Expression


In your groovy template, expressions are enclosed by "${}", E,g.
Hello ${who}

Rythm use "@" sign to mark an expression:
Hello @who

Smart guy will ask how to write the following expression in Rythm:
Jack is a ${vice}maniac.

Ok, here is how it looks like in Rythm:
Jack is a @(vice)maniac.

And similar for compound expressions:
@(foo.bar().numbers[5] + 16)

Note, to output the "@" sign, just put two "@" together:
John

1.1 Null-Safe Expression


I was impressed by the null-safe notation when I first read it and I found it's so handy to use, especially in the form html:
You don't need to say good-bye to null-safe expression, as Rythm brings you the same thing:

1.2 Expression escape


Like Groovy template engine, Rythm automatically escape expressions using html format. As a comparison, Japid won't automatically escape your expressions and require manually invoke the escape function to safely output expression: ${escape(expr)}.

However expression escape is not free, you sacrifice template rendering performance when you escape the expressions. So it's better for you to stop escaping while you are sure that part of your data is safe or you do what to output the HTML code. For a certain expression, use .raw() extension to stop auto-escape:
@html.raw()

If you want to output raw data for all expression in a segment of your template file, use @raw(){...} tag:
@raw() {
   None of the expression outputs will be escaped 
   including the @foo and @bar
}

2. Control flow


Unlike Groovy which use strange tag to expression control flow, Rythm use pure Java style to do it which makes it a compact, expressive, and more Java programmer friendly template language.

2.1 if condition


Groovy template:
#{if user.countryCode == 'en' }
    Connected user is ${user}
#{/if}

Rythm template:
@if ("en".equals(user.countryCode)) {
    Connected user is @user
}

2.2 if-else


Groovy:
#{if user}
    Connected user is ${user}
#{/if}
#{else}
    Please log in
#{/else}

Rythm:
@if(null != user) {
    Connected user is @user
} else {
    Please log in
}

2.3 if-else-if


Groovy:
#{if tasks.size() > 1}
    Busy tasklist
#{/if}
 
#{elseif tasks}
    One task on the list
#{/elseif}
 
#{else}
    Nothing to do
#{/else}

Rythm:
@if(tasks.size() > 1) {
    Busy tasklist
} else if (tasks.size() > 0) {
    One task on the list
} else{
    Nothing to do
}

2.4 loop


Groovy:
    #{list items:products, as:'product'}
  • ${product}
  • #{/list}
Rythm:
    @for(Product product:products) {
  • @product
  • }

All Groovy loop variables are also available in Rythm loop. For the case of the loop above, they are:
  • @product_index, the item’s index, starting at 1
  • @product_isLast, true for the last element
  • @product_isFirst, true for the first element
  • @product_parity, alternates between odd and even

In addition, Rythm provides one additional loop variable to make the parity identification be more convenient:
  • @product_isOdd, true for the odd order element

And to use those variables in your loop:
    @for(Product product:products) { @product_index. @product @(product_isLast ? "" : "-") }

3. Scripting and Comment


Like Groovy, Rythm provides facility to support inline scripting and comments in your template file, the differences is Groovy template engine use Groovy language to script while Rythm use pure Java lanaguage.

3.1 Comment


Groovy:
*{
    this is a block comment in Groovy template engine
}*

*{this is a line comment in Groovy template engine}*

Rythm:
@*
    This is a block comment in Rythm template engnine
*@

@*******************************************
* This is another block comment in Rythm
*******************************************@

@// this is a line comment in Rythm template engine

3.2 Scripting


Groovy use %{...}% and Groovy language to add dynamic logic into template:
%{
   fullName = client.name.toUpperCase()+' '+client.forname;
}%
 

Client ${fullName}


Rythm use @{...} and Java language to do the same work:
@{
   String fullName = client.name.toUpperCase() + " " + client.forname;
}

@****************************************************************
* Note, Groovy language use @{...} to do reverse url lookup, e.g:
* @{Application.index()}
*
* In Rythm you do reverse URL lookup using:
* @url(Application.index())
****************************************************************@

Client @fullName


In this article we briefly introduce how to migrate Groovy template to Rythm template step by step. We also inspected the essential usage of Rythm template including expression, flow control, scripting and comments.

In the next part I will introduce page layout management with @extend keyword, again it achieves all you have in Groovy and more. Stay tuned!

7 comments:

Bogdan Tanase said...

A very nice and welcome introduction to Rythm template, Gelin.

Although I haven't yet properly tried Rythm, it is very high on my TODO list and these type of guides will definitely help migrate.

Thank you, and keep up the good work!

Looking forward for the next part!

Gelin Luo said...

Thanks Bogdan. Now you have the second part - Layout management with @extend and @doLayout

romka said...

Wonderful!!
By the way, is it possible to use the Rythm with PlayFramwork 2.0?

Gelin Luo said...

At the moment I don't have a plugin for play2. However you can use Rythm as a pure Java library if you want to give it a try.

The pure Rythm engine could be found at https://github.com/greenlaw110/Rythm/downloads

Tulsi said...
This comment has been removed by the author.
Tulsi said...

Rythm is type-safe, right? So if I change something in the model or controller, how can I know if something in the templates has to be changed as well before hitting the page?

Gelin Luo said...

Play Rythm follow the template parameter passing convention in PlayFramework 1.x. And the template invoke mechanism itself is not typesafe.


However inside your template it is typesafe, because it compiles into Java class. Say, you cannot use groovy's dynamic feature inside your template.

About Me

My photo