Sunday, December 5, 2010

Grails Domain Objects Across Schemas

The Grails documentation is very clear on how to specify specific table and column names for your domain classes (in Section 5.5.2 Custom ORM Mapping). But what if you need to specify the schema as well? Fortunately it is just as easy, if less well-documented.

class Book {
static mapping = {
table name:'my_books', schema:'book_admin'
}
}
view raw book.groovy hosted with ❤ by GitHub


Of course, when all of your domain tables are in the same schema, you should simply specify a default schema in DataSource.groovy. The property is hibernate.default_schema.

Thursday, August 5, 2010

Testing rich webapps from JUnit with Rhino and Envjs

Envjs is an implementation of the DOM in Javascript. Together with Rhino, it can function as a headless web browser in Java. Picture HtmlUnit, but with flawless Javascript support. For instance, the latest version of JQuery works flawlessly in Envjs on Rhino but won't even load in HtmlUnit.

Here's a simple way to tie them together in JUnit. Wonderful testing frameworks to follow (looking at you, Mike ;)

Not too many Groovy-isms here, can easily be converted to plain Java.

package com.cadrlife.mywebapp;
import groovy.util.GroovyScriptEngine;
import org.apache.commons.lang.RandomStringUtils;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.ScriptableObject;
import static org.junit.Assert.*
public class MyWebAppTest {
private Context cx;
private ScriptableObject scope;
@Before
public void setup() throws Exception {
cx = ContextFactory.getGlobal().enterContext();
cx.setOptimizationLevel(-1);
cx.setLanguageVersion(Context.VERSION_1_5);
scope = cx.initStandardObjects();
// Assumes we have env.rhino.js as a resource on the classpath.
String envjs = getClass().getResourceAsStream("env.rhino.js").text;
String printFunction = "function print(message) {java.lang.System.out.println(message);}";
cx.evaluateString(scope, printFunction, "print", 1, null);
cx.evaluateString(scope, envjs, "env.rhino.js", 1, null);
// This will load the home page DOM.
run("window.location='http://localhost:5000'");
// Whatever script JS includes the home page has.
def libs = ["/js/jquery-1.4.2.min.js",
"/js/jquery-ui-1.8.2.custom.min.js",
"/js/app.js"];
for (String lib : libs) {
run(new URL("http://localhost:5000" + lib).text);
}
// Whatever happens on document ready.
run('appInit()');
}
@Test
public void navigation() throws Exception {
def h1Text = '$("h1:first").text()';
run('$(".nav .homeLink").click()');
assertJsEquals('Welcome', h1Text);
run('$(".nav .dashboardLink").click()');
assertJsEquals('Dashboard', h1Text);
}
private void assertJsEquals(String a, String b) {
assertEquals(a,run(b));
}
private void assertJsContains(String a, String b) {
String result = run(b);
assertTrue("${a} does not contain ${a}", result.contains(a));
}
private String run(String js) throws Exception {
Object result = cx.evaluateString(scope, js, "run", 1, null);
return Context.toString(result);
}
}

Friday, July 23, 2010

Watch Directory For Changes in Groovy

One must-have feature in any modern web stack is the ability to automatically restart/refresh the development server when you edit the source code. It is critical for developer workflow that the feedback loop be as tight as possible. Here source code refers to actual class definitions, templates (JSPs, GSPs, haml files, etc...), static content (javascript, css, images), AND configuration (I'm looking at you, struts.xml).

All web frameworks which do not support automatic updating of all content during development shall hereafter be referred to as "Legacy Web Frameworks". Is your shop using them?

Justin Voss and I have been hard at work on a web micro-framework called Ratpack (inspired by Ruby's Sinatra). Lest it become a Legacy Web Framework right out of the gate, we've implemented basic auto-reloading right away. Feel free to grab this for other uses, as it works independently.

groovy runapp.groovy app/myapp.groovy app

The script app/myapp.groovy will be killed and re-run when any content in the app directory changes.

Since Ratpack uses Jetty, there are probably better solutions. Feedback is welcome.

public abstract class DirWatcher extends TimerTask {
def path
def dir = [:]
// Exclude temp files created by vim, emacs, etc...
FileFilter fileFilter = {file -> !(file.name =~ /\.swp$|\~$|^\./)} as FileFilter
public DirWatcher(String path) {
this.path = path;
def files = new File(path).listFiles(fileFilter);
// transfer to the hashmap be used a reference and keep the lastModfied value
for(File file : files) {
dir.put(file, file.lastModified());
}
}
public final void run() {
def checkedFiles = new HashSet();
def files = new File(path).listFiles(fileFilter);
// scan the files and check for modification/addition
for (File file : files) {
Long current = dir.get(file)
checkedFiles.add(file)
if (current == null) {
// new file
dir.put(file, new Long(file.lastModified()))
onChange(file, "add")
}
else if (current.longValue() != file.lastModified()){
// modified file
dir.put(file, new Long(file.lastModified()))
onChange(file, "modify")
}
}
// now check for deleted files
def deletedFiles = dir.clone().keySet() - checkedFiles
deletedFiles.each {
dir.remove(it)
onChange(it, "delete")
}
}
protected abstract void onChange(File file, String action);
}
class AppRunner extends DirWatcher {
def proc = null
def script
AppRunner(String script, String path) {
super(path)
this.script = script
}
def manageApp() {
runApp()
Timer timer = new Timer()
timer.schedule(this, new Date(), 1000)
}
def runApp() {
proc = "groovy ${script}".execute()
proc.consumeProcessOutput(System.out, System.err)
}
def killApp() {
proc.waitForOrKill(1000)
}
void onChange(File file, String action) {
println ("File "+ file.name +" action: " + action )
if (proc) {
println "KILLING"
killApp()
println "RELOADING"
} else {
println "STARTING"
}
runApp()
}
}
if (args.length == 2) {
new AppRunner(args[0], args[1]).manageApp()
} else {
println "Usage:"
println "groovy runner.groovy [script] [dir to watch]"
}
view raw runapp.groovy hosted with ❤ by GitHub


The NIO.2 Filesystem in JDK7 will make this sort of thing much easier.

Sunday, May 9, 2010

Haml For Grails

Writing a Grails app? Find out why so many Rubyists swear by Haml for writing views.

<!-- GSP/JSP -->
<div id="profile">
<div class="left column">
<div id="date"><%= date %></div>
<div id="address"><%= user.address %></div>
</div>
<div class="right column">
<div id="email"><%= user.email %></div>
<div id="bio"><%= user.bio %></div>
</div>
</div>


/ Haml
#profile
.left.column
#date= date
#address= user.address
.right.column
#email= user.email
#bio= user.bio

Interested parties have created JHaml, a Java implementation of Haml, and a corresponding Grails plugin. (Patches welcome!)

To try it out, just grab the plugin:
grails install-plugin haml
Then add this bean definition to your grails-app/conf/spring/resources.groovy.

beans = { // ...
groovyPageResourceLoader(com.cadrlife.jhaml.grailsplugin.HamlGroovyPageResourceLoader) {
baseResource = new org.springframework.core.io.FileSystemResource(".")
}
}

Now, you have the option of writing views (with the .haml extension) that will be automatically rendered to GSPs.

Learn more in the Haml tutorial.

Official Grails Plugin Page

Sunday, April 18, 2010

"Holy Grail" Three-Column Layout in Haml and Sass

Thanks to Matthew Levine for his article In Search of the Holy Grail.

!!! Strict
%html(lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml")
%head
%title Page Title
%meta(http-equiv="Content-Type" content="text/html; charset=UTF-8")
= stylesheet_link_tag :all
= javascript_include_tag :all
= csrf_meta_tag
%style{ :type => "text/css" }
:sass
!lc_width = 200px
!rc_width = 150px
!min_width = !lc_width * 2 + !rc_width
body
min-width = !min_width
#container
padding-left = !lc_width
padding-right = !rc_width
.column
position: relative
float: left
#center
width: 100%
#left
width = !lc_width
right = !lc_width
margin-left: -100%
#right
width = !rc_width
margin-right = -!rc_width
#footer
clear: both
/*** IE6 Fix ***/
* html #left
left = !rc_width
%body
#header
#container
#center.column
= yield
#left.column
#right.column
#footer


Feel free to post your own variants!