Friday, November 27, 2009

Easy in-place delete with ajax and grails

I was recently asked to implement new delete functionality on a list of items. The catch of this request was that the removal of an item would be done without reloading the page. This required me to write a boatload of code in both JavaScript and Java to successfully implement. After this somewhat painful experience, I wanted to see how elegantly this experience would be handled in grails. I was not disappointed.

I started by following the grails quick-start. After creating my book class, I chose to use the grails generate-all command to create the controller and views all at once.

With the auto-generated scaffolding done, the next thing we need to do is find this line in list.gsp:

<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">

and change it to the following:

<tr id="book-${bookInstance.id}" class="${(i % 2) == 0 ? 'odd' : 'even'}">

This adds an ID to each row of the list (book-1, book-2, etc.) so that we can easily identify it from javascript. By default, grails has the prototype library available, but we are going to add support for scriptaculous which will give us some slick effects to work with. This can be done by adding the following to /grails-app/views/layouts/main.gsp:



To handle the new AJAX delete functionality, we will need to add a new delete method to our controller. The following method will redirect the request to the list() method if no book exists for the provided id, or return plain text describing the result of the operation if one is found.

def ajaxDelete = {
  def book = Book.get( params.id )
   if(bookInstance) {
   try {
    bookInstance.delete(flush:true)
    render "Book ${params.id} deleted"
   } catch(org.springframework.dao.DataIntegrityViolationException e) {
    render "Book ${params.id} could not be deleted"
   }
  } else {
   flash.message = "Book not found with id ${params.id}"
   redirect(action:list)
  }
 }

To connect our user interface with the newly created ajaxDelete method we will use the fantastic tag. Add a new column to display our delete button by adding the following code to list.gsp between the
tags that are nested in the tag generated by the grails scaffolding.


delete

This tag binds the link to the BookController.ajaxDelete() method, passing the id of the current book as a parameter. The update parameter instructs grails to inject the results from a success message into the 'message' div, and any failure into the 'error' div. I have instructed the onComplete to invoke a custom bookRemoved() method, and to pass the id of the current book as a parameter. We will use the passed id to determine which row to remove on a successful deletion. Here is the code that works in tandem with the defined earlier.

<div id="message" class="error"></div>
<div id="error" class="error"></div>

 function bookRemoved(bookId) {
  Effect.toggle('book-' + bookId, 'appear');
 }


With this last bit of code complete, you can start the server and view the new AJAX delete button in action!

Thursday, November 19, 2009

Invoke test classes from groovy

Just a quick note, mostly for myself. If a unit test extends GroovyTestCase, it can be run from groovy.

class GroovyTest extends GroovyTestCase {
    void testString {
        assertEquals 'Test failed!', "Test", "Test"
    }
}

To execute this test, run the following command:

$ groovy GroovyTest.groovy
 Time: 0.125
 OK (1 test)

Friday, November 13, 2009

Grails, validation, constraints, oh my!

I must admit that I am continually impressed by the ease with which grails makes previously tedious tasks simple and easy. In my current project I found it necessary to enforce a constraint that a date be in the future. My first reaction was along the lines of "no problem, I'll just validate the data in the business tier", and then luckily discovered a much more elegant solution in grails: constraints.

The grails reference defines a constraint as "Allows the definition of declarative validation constraints". Grails comes built in with numerous constraints that fit most day to day needs: url, email, credit card, and many more! To define a set of constraints you only need to define a constraints block.

For example, imagine you need a "User" domain object, requiring a "name", and "email" properties. Our business rules dictate that the name property must not be blank, be between 3 and 30 characters, and the email address provided must be a valid email address. This class (and the required validation) would look like the following:

class User {
    static constraints = {
        name(size:3..30, blank: false)
        email(email: true)
    }

    String name
    String email
}

If these constraints are not enough you are provided with the ability to utilize a closure and write anything you need. Here is an example of a custom validator:

class User {
    String login
    static constraints = {
        login(validator: {
            if (it.contains('admin')) return ['invalid.login']
        })
    }
}

If you wish to define a constraint that can be used across classes you can implement the Constraint interface. I chose to extend AbstractConstraint which takes care of some of the underlying boilerplate code that would need to be written. Here is a fictional example which defines an isFred constrains which is applied to String values, and validates that the provided string does not contain "fred".

class IsFredConstraint extends AbstractConstraint {

    private boolean fred;

    public boolean supports(Class type) {
        return type != null && String.class.isAssignableFrom(type);
    }

    public void setParameter(Object constraintParameter) {
        if (!(constraintParameter instanceof Boolean))
            throw new IllegalArgumentException("Parameter for constraint [isFred] of property [" + constraintPropertyName + "] of class [" + constraintOwningClass + "] must be a boolean value");

        this.fred = ((Boolean) constraintParameter).booleanValue();
        super.setParameter(constraintParameter);
    }

    public String getName() {
        return "isFred";
    }

    protected void processValidate(Object target, Object propertyValue, Errors errors) {
        if (fred) {
            String value = propertyValue.toString();
            def args = (Object[]) [constraintPropertyName, constraintOwningClass, propertyValue]

            if (value.contains("fred")) {
                super.rejectValue(target, errors, "invalid.fred", "not.isFred", args);
            }
        }
    }
}

Any file ending in "Constraint" will auto-magically be added as a constraint so name the above file "FredConstraint.groovy" and place it in the /grails-app/utils directory. It can then be used like so:
static constraints = {
      login(isFred:true)
    }

This is my first foray into grail validation, so if there is a better approach please let me know in the comments!

Saturday, November 7, 2009

Grails 1.1, integration tests, and unsaved transient instance errors

In the grails documentation, and every book on the subject you will find an example similar to:

class Author {
    static hasMany = [ books : Book ]
    String name
}

class Book {
  static belongsTo = Author
  Author author
  String title
}

The belongsTo reference ensures that saves, updates, and deletes are cascaded. All the information that I have found provides an example to save your domain classes like:

new Author(..)
       .addToBooks(new Book(..))
       .save()

During recent development, I was creating an integration test on a model very similar to this and kept seeing errors that stated "object references an unsaved transient instance -  save the transient instance before flushing". Pouring over my code, everything looked correct - all the relationships were correctly defined, it was simply refusing to save properly. In the end I got lucky with a google search and found that there is a bug in 1.1 that only affects integration tests (and is fixed in 1.2RC1). If you update your save to:

new Author(..)
       .addToBooks(new Book(..))
       .save(flush:true, validate:false)

the save will cascade correctly.

Friday, November 6, 2009

The Grails journey begins

After working with ColdFusion and becoming frustrated with some of it's shortcomings, I decided to use a "grown up" language; Java.  It's been quite a few years since I made the leap to Java, and it has been a very interesting journey.  I have had the "pleasure" to work with many different frameworks, tools, and technologies.  With each I began with the same goal: to find an easier way to do X, from looking for an easier way to parse XML (JAXB, XmlBeans, etc.) to something as trivial as sending an email (JavaMail, Spring's emailTemplate, velocity for templating, etc.), I would always think back to just how "easy" it was to do many of these mundane tasks in ColdFusion.

Every time I would run into one of these challenges I would begin an internal debate about whether I should return to CF.  Each time the same objections came up: IDE support was horrible compared to java, ORM support was immature for EE development, and a big one being the cost of obtaining a CF license for my projects.  Yes, there are open source CF engines, but at the time they were immature and I did not feel they were production ready.  Each time I would choose Java, and selected a framework to build against (most recently the full Spring 3.x stack).  With the release of CF9,  ColdFusion now has built in ORM support (it uses hibernate), and decent IDE support (CFBuilder), but the costs remain.  So I still end up choosing java.

The part that is the most challenging to me with Java is the requirement to have knowledge in so many varied technologies to get a "RAD" environment up and running successfully.  As an example i'll use my last project which was based on Spring.  I wanted to build a web application, backed by a database, and deploy it on tomcat.  Seems simple right?  Getting a working configuration took many hours of scouring the internet, reading (very well written) spring docs, books, and fiddling.  Which TransactionManager to use?  Which DataSource implementation?  Should I configure the OpenSessionInView filter?  Many of these questions I have spent years learning the answers to, but the pain to a new developer is enormous, and many times fatal.  While each of these questions are needed to allow for maximum flexibility, they force a required level of knowledge on the end user.  When I look back to my CF days, to connect to the database it was a simple matter to define a datasource, and then use - a much different experience to the user.

Ruby on Rails (RoR) has shown how a framework paired with a dynamic language can offer extreme productivity gains while reducing the knowledge that a developer must possess to be effective.  I feel that if I move to RoR that I would lose many years experience in Java to gain rapid productivity, instead of leveraging a dynamic language based on the JVM.

In the end I chose to take the dive into Grails and Groovy.  This blog will follow my explorations, challenges, missteps, and (hopefully) successes.

Cheers,
Rich