Simple CRUD with Gaelyk
October 21, 2010 at 7:26 pm | Posted in GAE, Groovy, Search | 5 CommentsTags: App Engine, Big Table, cloud, GAE, GAE/J, Gaelyk, Google, Groovy
After having written a Grails application for Google App Engine I know exactly what the pain points are in production: Cold startup times make using the application an unenjoyable experience. App Engine calls for a lightweight application with far less JAR baggage and fast startup times. Yesterday I attended Guillaume LaForge’s session “Gaelyk, a lightweight Groovy toolkit for Google App Engine” at SpringOne2GX presenting the nuts and bolts of Gaelyk. The session was reason enough to think about rewriting my application to Gaelyk. To get started I whipped up a simple CRUD example in Gaelyk. All you need to do is to download the Gaelyk template application and you are ready to go. The test application will have four simple use cases: show all entities of a certain type (in my case a “Goal” entity), create new ones, update and delete them.
Gaelyk doesn’t support multiple closures in a controller class to automatically expose URLs by convention like Grails does. Instead you have to define your URL routing for each operation separately.
war/WEB-INF/routes.groovy:
get "/goals", forward: "/showGoals.groovy" get "/goals/add", forward: "/WEB-INF/pages/goal.gtpl" post "/goals/insert", forward: "/insertGoal.groovy" get "/goals/delete/@id", forward: "/deleteGoal.groovy?id=@id" get "/goals/edit/@id", forward: "/editGoal.groovy?id=@id" post "/goals/update", forward: "/updateGoal.groovy"
To show all Goals I defined a Groovlet named showGoals.groovy that uses the App Engine datastore low-level API to retrieve them and forward to the view goals.gtpl. In the view you can add a new goal or edit/delete existing goals.
war/WEB-INF/groovy/showGoals.groovy:
import com.google.appengine.api.datastore.Query
import com.google.appengine.api.datastore.PreparedQuery
import static com.google.appengine.api.datastore.FetchOptions.Builder.*
log.info "Getting all goals"
def query = new Query("Goal")
PreparedQuery preparedQuery = datastore.prepare(query)
query.addSort("created", Query.SortDirection.DESCENDING)
def goals = preparedQuery.asList(withDefaults())
request.goals = goals
forward '/WEB-INF/pages/goals.gtpl'
war/WEB-INF/pages/goals.gtpl:
<% include '/WEB-INF/includes/header.gtpl' %>
<h2>Goals</h2>
<a href="/goals/add">Add Goal</a>
<br><br>
<table border="1">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Created</th>
<th> </th>
</tr>
</thead>
<tbody>
<% request.goals.each { goal -> %>
<tr>
<td>${goal.name}</td>
<td>${goal.description}</td>
<td>${goal.created}</td>
<td><a href="/goals/delete/${goal.key.id}">Delete</a> | <a href="/goals/edit/${goal.key.id}">Edit</a></td>
</tr>
<% } %>
</tbody>
</table>
<% include '/WEB-INF/includes/footer.gtpl' %>
The operations insert and edit share a common view named goal.gtpl. Based on whether the request contains the attribute goal the view will be in insert or edit mode. In case an existing Goal entity was provided the form gets populated with the entity data and is ready to be updated. The Goal to be edited will be retrieved by providing the entity’s ID in the editGoal.groovy Groovlet.
war/WEB-INF/groovy/editGoal.groovy:
import com.google.appengine.api.datastore.KeyFactory
import com.google.appengine.api.datastore.Key
log.info "Editing goal"
def id = Long.parseLong(params.id)
Key key = KeyFactory.createKey("Goal", id)
def goal = datastore.get(key)
request.goal = goal
forward '/WEB-INF/pages/goal.gtpl'
war/WEB-INF/pages/goal.gtpl:
<% include '/WEB-INF/includes/header.gtpl' %>
<%
def goal = request.getAttribute("goal")
boolean existingKey = goal?.key
String action = !existingKey ? 'Add' : 'Update'
%>
<h2>${action} Goal</h2>
<form action="/goals/${!existingKey ? 'insert' : 'update'}" method="POST">
<table border="0">
<tbody>
<tr>
<td>Name:</td>
<td><input type="text" name="name" value="${goal?.name ? goal.name : ''}"></td>
</tr>
<tr>
<td>Description:</td>
<td><input type="text" name="description" value="${goal?.description ? goal.description : ''}"></td>
</tr>
</tbody>
<% if(existingKey) { %>
<input type="hidden" name="id" value="${goal.key.id}">
<% } %>
</table>
<br>
<input type="submit" value="${action}">
<input type="button" value="Cancel" onclick="javascript:document.location.href = '/goals';">
</form>
<% include '/WEB-INF/includes/footer.gtpl' %>
At last we need to implement the Groovlets for persisting the changes to your entity. All of them are quite straight forward to implement. Gaelyk’s abstractions for the datastore work quite nicely.
war/WEB-INF/groovy/insertGoal.groovy:
import com.google.appengine.api.datastore.Entity
log.info "Inserting goal"
def goal = new Entity("Goal")
goal.name = params.name
goal.description = params.description
goal.created = new Date()
goal.save()
redirect '/goals'
war/WEB-INF/groovy/updateGoal.groovy:
import com.google.appengine.api.datastore.KeyFactory
import com.google.appengine.api.datastore.Key
log.info "Updating goal"
def id = Long.parseLong(params.id)
Key key = KeyFactory.createKey("Goal", id)
def goal = datastore.get(key)
goal.name = params.name
goal.description = params.description
goal.save()
redirect '/goals'
war/WEB-INF/groovy/deleteGoal.groovy:
import com.google.appengine.api.datastore.KeyFactory
import com.google.appengine.api.datastore.Key
log.info "Deleting goal"
def id = Long.parseLong(params.id)
Key key = KeyFactory.createKey("Goal", id)
key.delete()
redirect '/goals'
5 Comments »
RSS feed for comments on this post. TrackBack URI
Leave a Reply
Blog at WordPress.com. | Theme: Pool by Borja Fernandez.
Entries and comments feeds.

Good write up. You might even shame me into writing up my own experiences!
One thing to note is that when you are taking param arguments and adding them to your entity with the same name you can use the following groovy sugar:
goal << params.subMap(['name', 'description'])
P.
Comment by @parker0phil— October 22, 2010 #
Nice write up!
-Ben
Comment by Ben Carlson— November 1, 2010 #
Great post – I found this really useful for my first gaelyk app.
Comment by Barry— February 9, 2011 #
You can also use this sugar for your goals:
params.with {
goal.name = name
goal.description = description
}
or
goal.with {
name = params.name
description = params.description
}
Comment by crazy4groovy— March 16, 2011 #
Very helpful, thanks!
Comment by Loic Descotte (@loic_d)— August 6, 2011 #