Tuesday, May 05, 2009

Converting legacy Rails apps to Grails : The Controllers

So, you've already looked at the previous blog posts on Setting up the Project and Migrating the Domain Objects. The whole world must be wondering "What happened to this blog post series, did people just stop migrating from Rails to Grails?". Well, I've been in Tapestry land for the last 6-7 months and haven't had much free time to finish my blogging endeavor to finish my series of articles. But, what do you know : all of a sudden the topic of migrating legacy Rails apps to Grails came back to the fore for me (work related, don't ask, it's top secret), and here I am. In a valiant effort, I will try to finish off the topic in one fell swoop (hopefully today) and bang out a couple of different articles that document in details the ups and downs of such a migration.

Because this article is on the long side, here is the Table of Contents:

  1. Overview of Controllers

  2. General language related issues.

  3. Input Processing

  4. Input Validation and Error Reporting

  5. Rendering Responses

  6. Advanced Ajaxiness : Dynamic Javascript

  7. Conclusion




1. Overview of controllers






Now, back to the meat and potatoes of this article : migrating the Rails controllers to Grails. It's no secret that Grails heavily borrowed ideas from Rails (and NO, Grails is not Groovy on Rails, there's no mass transit involved at all, it's the good ole cup that everyone wants) and as can be seen from the screenshot of the project setup, both framework keep the controllers in the Controllers NetBeans project folder. Creating Grails controllers is easy: just right click on the Controllers project folder and select "New Controller". NetBeans walks you through naming the Controller properly and creates the needed file and run the regular Grails "create-controller" task, which in creates a default view for the controller.






The structure of the controllers themselves is very similar as well : in both cases, there is a one-to-one relationship between the Rails and Grails controllers. Inside the controller, in both cases, there is a class containing a bunch of closures , methods, and private member variables. In both cases, the closures in the controller become a part of the "public api" exposed by the controller, as all closures can be called from the URL (e.g. http://localhost:8080/app/controllerName/closure -> http://localhost:8080/app/account/login). Private methods are not accessible to be invoked from the URL. For "old school" Java developers who might not be intimately familiar w/ Rails or Grails, it is interesting to note that the controllers are thread safe : that is, they can contain instance variables that will not be clobbered if two concurrent requests are sent to the same controller. A new controller instance is created for each Http request.

OK, let's see what's inside the controller. Here's an example Rails controller:


class ActivityItemsController < ApplicationController
def create
@activity_item = ActivityItem.new(params[:activity_item])
if @activity_item.save
// do whatever
else
// do whatever else
end
end
end


Converting this same controller to Grails would look like this:

class ActivityItemsController extends ApplicationController {
def activity_item;

def create = {
this.activity_item = new ActivityItem(params.activity_item)
if (activity.save()) {
// do whatever
} else {
// do whatever else
}
}
}



2. General language related issues.

A lot of things to talk about here. First of all, just looking at the code it looks almost the same. The first superficial difference is the naming convention for the classes : in Rails user underscore_separated_file_names, whereas Groovy uses CamelCase. One notable difference is that in Grails, you do need to declare the class members, whereas in Rails (due to Ruby heritage), the properties can directly be assigned to when needed (e.g. @activity_item = ....). While the Ruby approach does save one line of code to declare the property, while migrating the code I found it very helpful to see the declarations at the top of the Groovy class. When you don't declare the class members upfront, it seems that it's quite easy to create a whole bunch of properties in the Ruby class w/o realizing how many you've created, which generally can lead to muddying the interface (mind you, the said properties are publicly accessible - e.g. from views, other closures, methods, etc).

3. Input processing
The second thing to note is the existence of the "params" map in both cases. In both cases, one can both read from and write to the params map using the accepted syntax : map[:key] in Ruby and map.key or map[key] in Groovy. So, nothing particularly interesting here. A bunch of other default objects are available in the Grails controller (probably quite familiar to Java Devs) such as servletContext, session, request, params, flash. Dealing with all is mostly the same in both frameworks and should be familiar to anyone done anything on the web.

When processing input, in a number of places, Rails uses the following shortcut/idiom to bulk update the values of many attributes of an object from request parameters at once:

class Foo

def bar
@activity.update_attributes(params[:activity])
end
end

To cut the long story short, this take in the value of request parameters and binds them to values in the object (note, this has severe security implications but that's a different topic to discuss). Grails offers an equivalent statement with:

class Foo {
def activity

def bar = {
bindData(activity,params.activity)
}

}

If using the straight out bindData method from Grails it accomplishes the same thing, with the same security implications. Whenever I actually bumped into examples like this, I tried to address some of the security issues by using the "more advanced" bindData method in Grails, which allows specifying parameters to exclude and a prefix of a property to use for binding, e.g. if I only wanted to bind the customer.name and customer.phone attributes from the request and I definitely wanted to prevent the customer.id attribute being affected, I'd use something like this:

bindData(myCustomerObject,params,["id"], "customer")



4. Input Validation and Error Reporting
In both framework, a large part of validating the input that is written to the domain model is done by specifying constraints in the domain model itself (e.g. see the article about the Rails->Grails domain migration). Thus, in both frameworks, code like this is pretty common:

if @activity.save
// do whatever on success
else
flash[:error]= @activity.errors.full_messages.join("
")
end


In Grails, the code looks very similar:

if (activity.save()) {
// do whatever on success
} else {
flash.error = activity.errors
}


One minor difference here is that (at least in this app), the Rails just concatenated the error messages as text and placed them in the "error" property in flash scope. In contrast, Grails assigns the actual "errors" object to the same flash property, then allowing the view to render these error objects as it wishes (e.g. using the g:renderErrors tag), which would allow rendering an error for a particular property, etc.

In both frameworks, validation of can happen in the controller itself, and errors can be added to the relevant error property (in the appropriate scope).

One more advanced feature of Grails that I found very useful later on in the conversion are the Grails are the form beans that you can use to populate values from the request (thus shielding from the security issues referred to further up), validating the input in a domain-class style approach, and generating errors in a nice and easy manner. So, here's the form object:


public class ChangePasswordForm {
String oldPassword;
String password;
String passwordConfirmation;

static constraints = {
oldPassword(nullable:false,blank:false)
password(nullable:false, blank:false, size:4..40)
passwordConfirmation(nullable:false, blank:false, size:4..40,
validator: { oldPw, chgPwdCmd ->
if (oldPw!=chgPwdCmd.password) {
return "notsame.message"
}
}
)
}
}


You'll note the declarative syntax familiar from domain object validation, it's a beauty !!!

<g:formRemote url="[action:'change_password']" name="ChangePasswordForm"
before="Element.show('form-indicator-pwd')"
onSuccess="Element.hide('form-indicator-pwd')">

</g:formRemote>


Add the following to your grails-app/i18n/messages.properties for custom error messages:

#Custom messages
forms.ChangePasswordForm.passwordConfirmation.notsame.message=Password confirmation not the same as password
wrong.password.message.forms.ChangePasswordForm.password=Old password is wrong, please enter again
forms.ChangePasswordForm.password.blank.message={0} cannot be blank


Finally, using the form in the controller when submitted:

def change_password = { ChangePasswordForm changePasswordForm ->
if (!changePasswordForm.hasErrors()) {
// accessing the values from the form
def pwdValue = changePasswordForm.password
// adding a custom error to the form for an error not enforced in constraints
if (whateverRandomReasonYouWantToRejectAField) {
changePasswordForm.errors.rejectValue("password","wrong.password.message")
}
// do whatever
} else {
// do whatever else
}
}
}




Finally, it seemed like a pretty common idiom in the Rails app to use dynamic javascript (I will talk about that plugin later ) to render errors back to the client:

if (@activity.save)
// do whatever on success
else
flash[:error] = "#{@activity.errors.full_messages.join('
')}"
render :update do |page|
page.replace_html "errors_div", :partial => "common/errors_flash",:layout=>false
// do whatever else to the page
end
end


In essense, this takes the validation errors, renders them using the "errors_flash" template, and replaces the content of the "errors_div" in the page with the rendering result. This approach caused me a lot of grief initially, but after a little bit of work it turned into the following in my Grails app:

if (activity_item.save()) {
// do whatever on success
} else {
js_error(activity_item.errors)
}


Where the js_error method in the controller superclass, looks something like this (using the dynamic javascript plugin that will be discussed later):

def js_error = { errors ->
flash.error = errors
log.debug "Sending errors back to client: ${errors}"
renderJavascript {
update 'errors_div', [text:g.render(template:"/common/errors_flash")]
callFunction "Element.show" , 'errors_div'
}
}


5. Rendering Responses
Converting the response rendering from Rails to Grails was pretty straightforward, here's the Rails example:

def new
render :partial => "new", :layout => false
end


In Grails, this becomes:

def _new = {
render(template:"new")
}


One tricky thing to note here is that because "new" is a Groovy keyword, I could not use the same closure name, NBD. The render controller method is pretty much the best thing since sliced bread and can render a whole bunch of things like regular pages, templates, XML, or JSON. Grails uses a convention that partial pages (templates) are named starting w/ an underscore. Thus, when you do:

render(template:"fooTemplate")

Grails finds the _footemplate.gsp file and renders it (equivalent to the Rails render :partial => "footemplate" which renders _footemplate.rhtml).


One other common idiom in the Rails app was to issue redirects from the controller:

redirect_to :action => 'show', :controller => 'activities', :id=> params[:act_id]


Grails supports this idiom pretty nicely with the redicect controller method with pretty much the same parameters:

redirect(controller:'activities', action:'show', id:params.act_id)



This particular project was using both script-centered and content-centered AJAX, and not much data-centered AJAX, so I didn't get to use JSON or XML rendering much; however, I always found the automatic marshalling to JSON or XML pretty cool:

// Automatic marshalling of XML and JSON
import grails.converters.*

render Book.list(params) as JSON
render Book.get(params.id) as XML




Finally, one cool feature of Rails that I initially missed was the ability to specify the default layout per controller :

class ActivitiesController < ApplicationController
layout "internal"
end

The statement above sets the default layout for this controller in the controller itself. Grails supports specifying the layout either by convention (e.g. grails-app/views/layouts/CONTROLLER.gsp or grails-app/views/layouts/CONTROLLER/ACTION.gsp , effectively equivalent to specifying layout="internal" in the controller) or explicitly in the template by specifying a meta tag (<meta name="layout" content="internal"></meta<) in the template.

6. Advanced Ajaxiness : Dynamic Javascript
I was planning to discuss Rails plugins in a separate article; however, there is one particular Grails plugin that was extremely useful to cover a portion of Rails that Grails doesn't cover out of the box. More specifically, I'm talking about the Rails script centered AJAX w/ dynamic javascript, e.g. :

render :update do |page|
page.replace_html "errors_div", :partial => "common/errors_flash",:layout=>false
page.replace_html "show_activities", :partial=>"show", :layout=>false
end


As explained before, this replaces the content of the "errors_div" in the page w/ the rendered "errors_flash" template (which basically renders the flash errors in a list or something like that), and then replaces the content of the "show_activities" div w/ the content of the partial template.

My initial approach was to change the actual pages to process the response and update the right div w/ the returned content, but it was a big PITA, considering how much the Rails app used this. Finally, after some searching on the net, I found the Dynamic Javascript plugin. It had most of the features I needed to implement this Rails idiom with something like this:


renderJavascript {
update 'errors_div', [text:g.render(template:"/common/errors_flash")]
callFunction "Element.show" , 'errors_div'
update 'show_activities', [text:g.render(template:"show_activities")]
}


Now, in places where I needed to update multiple elements on the page, I ended up using this style of code in the controller itself. However, the majority of the uses were the following:
* Render an error div
* Replace a content div w/ the content of a template
* A combination of the two.

As a result, I moved the following code into the parent class of my controllers:

def js_error = { errors ->
flash.error = errors
log.debug "Sending errors back to client: ${errors}"
renderJavascript {
update 'errors_div', [text:g.render(template:"/common/errors_flash")]
callFunction "Element.show" , 'errors_div'
}
}

def js_render = { replaceDiv,replaceContent ->
renderJavascript {
replace replaceDiv, [text:replaceContent]
}
}

def js_error_and_replace = { errors, replaceDiv,replaceContent ->
flash.error = errors
println "Found errors ${errors}"
renderJavascript {
update 'errors_div', [text:g.render(template:"/common/errors_flash")]
callFunction "Element.show" , 'errors_div'
replace replaceDiv, replaceContent
}
}



Which significantly simplified the code in the actual controllers, e.g. :

if (this.user.authenticate(user.login, changePasswordForm.password)) {
if (this.user.save()) {
flash.notice = "Password Changed"
js_render("change_password", g.render(template:"change_password_form"))
} else {
flash.notice = "Password could not be changed"
js_error(user.errors)
}
} else {
println "Didn't authenticate w/ password"
js_error_and_replace(changePasswordForm.errors,"change_password",[text:g.render(template:"change_password_form")])
}


As mentioned above (and as expected), Grails support redirects pretty nicely. However, Javascript redirects , though not necessarily tricky, still required an extra piece of code to the parent controller:

def js_redirect = { redirectUrl ->
def jsRedirect = "document.location = \'$redirectUrl\'"
renderJavascript {
appendJavascript jsRedirect;
}
}

At which point, the code in the controller is really straightforward:

js_redirect(createLink(controller:'activities', action:'show',id:params.id))


However, one of the use cases involved a form executing in a frame (an upload form that did some status updates in a frame), which depending on the content returned in the frame, needed to redirect the whole browser window whenever the upload was done. In Rails it was handled as such:

responds_to_parent do
render :update do |page|
flash[:notice] = "File Uploaded Sucessfully"
page.redirect_to :action =>'show', :controller=>'activities', :id=>params[:activity_id]
end
end



The code in the parent controller to support this idiom is as follows:

def parent_redirect = { redirectUrl ->
render (text : "<html><body>"+
"<script type='text/javascript' charset='utf-8'>"+
"var loc = document.location;"+
"with(window.parent) {"+
" setTimeout(function() { "+
"window.eval(document.location = \'$redirectUrl\'); loc.replace('about:blank'); "+
"}, 1)"+
"}"+
"</script></body></html>")
}


One final note on the Dynamic Javascript plugin : the plugin that is uploaded on the Grails wiki is version 0.1 . The plugin appears to not be maintained actively; however, the linked author's blog (http://blog.peelmeagrape.net/2007/10/9/dynamic-javascript-plugin-for-grails) has version 0.2 of the plugin. Still, when I was heavily using this plugin, I ran into some issues with it and had to patch it as follows (in plugins/dynamic-javascript-0.2/src/groovy/JavascriptBuilder.groovy , commented out code is the broken part):

private String renderTemplate(Map options)
{
// StringWriter s = new StringWriter()
// GrailsWebRequest webRequest = (GrailsWebRequest)RequestContextHolder.currentRequestAttributes();
// HttpServletResponse response = webRequest.getCurrentResponse();
// def writer = (RoutablePrintWriter)webRequest.getOut()
return controller.g.render(options);
// writer.destination.out.toString();
}


I posted on the author's blog w/ the proposed change, and later on received a notification that he liked the change and that he would incorporate it into the plugin. However, it appears that the plugin on his page is still at version 0.2 and my comment has disappeared from the blog. Oh, well...


Conclusion
It was quite a journey so far. None of the problems in migrating controllers are particularly difficult or mind bending; however, there are just a lot of different issues to deal with if you're starting from scratch. So, now that you have all this good info, START MIGRATING THAT RAILS APP THAT YOU'VE BEEN EYEING, WOULD YA !!???!!!