Motivation

When trying to iteratively develop applications, it is typical that your domain model changes a lot during early parts of development. Scaffolding is a great crutch to develop CRUD pages, and in fact the scaffolding process can be extended to add additional pages beyond the regular CRUD provided out of the box. Furthermore, Grails' capability to do dynamic scaffolding is a great way to test your domain model, and frequently can serve as basis for future work on GSP pages.

However, the default scaffolding templates are limiting in that they don't allow customizations of scaffolded pages. For example, if I want to NOT display a particular property on the show page, I am not able to do so - I have to generate the scaffolded GSP pages, and then manually edit them. This means that if I need to add another property in the future, I have to either start manually edit the GSP pages, or regenerated scaffolded pages, and remove the unwanted property. This seems to violate DRY.

Additionally, some plugins require modifying the GSP pages. For example, the drill-down plugin requires to add <drilldown:resources/> tag to the header element, as well as other elements into the list table. Our plugin had to provide ability to insert some of those customization so that we can insert plugin integration code into our scaffolded pages.

I wanted solutions to the problems above, and wanted to see how far I can push the dynamic scaffolding process in that my UI layer is absolutely minimal. There will definitely be time for custom UI development, but I wanted to generate as much of the UI as possible.

Introduction

Let's step back for a second. Using dynamically scaffolded pages is a two step process. The first part of the process generates the pages, and the second part executes them agains the application domain model. Our goal is to provide hooks in the first phase of the process, so that the generated (and executed) pages are modified appropriately.

If you examine the scaffolding process, you will see that there are typically two types of insertion points. The first insertion point is fairly static, for example the header of a page. If we have an ability to inject some code into a header, then we can modify the page. Another type of insertion point is when the scaffolding pages iterate over properties of a domain object. This allows us to customize a particular row in the table that displays properties. If we want to not display a particular property, or to modify how it gets rendered,

The goal of the design was to provide a very flexible way to define scaffolding extensions definitions. Because the users may prefer to define their extension definitions differently, we want to enable that flexibility.

As a result, there are many different ways to do it, including one that can be easily put along the constraints definitions during domain object creation, which allows a convenient way to view the definitions without creating unnecessary coupling.

Installation

To install the UberScaffolding plugin type this command from your project's root folder:

grails install-plugin uber-scaffolding

Basic Example Of Usage

Let's say you have a regular Book / Author application. Suppose you are happy with the Grails scaffolding for the book page, but want to add a help message to on top of the list page, say to announce that there is a sale.

You can go into list.gsp scaffolding template, and insert a piece of code on top of the page:

This declares a scaffolding helper object that you can then use (with the name of current class). className will resolve to "Book". Find the place inside list.gsp that you want to inject some code and add this code:

<% out << scaffoldingHelper.optionalCustomHtml("listPageTop") %>

This declares an area called "listPageTop" that you can now use to inject code. You are done with the list.gsp modifications for now.

Next, somewhere in your code, say bootstrap, you need to create a Scaffolding Helper for Book class.

Other Defined Constants

UI_PROPERTY_LIST_ONLY - only display the property on list page
UI_PROPERTY_CREATE_ONLY - only display property on create page
UI_PROPERTY_EDIT_ONLY - only display property on edit page
UI_PROPERTY_SHOW_AND_EDIT - only display property on show and edit page
UI_PROPERTY_LIST_AND_SHOW - only display property on show and list page, useful for readOnly properties
UI_PROPERTY_NO_LIST - display the property everywhere, but not the list page

Debugging

It is useful to examine the generated pages, especially if there are bugs. The pages get generated on-demand, so if you open a page list, that one gets generated, others don't. To examine generated pages, a special page showGSPs exists for each controller (use it instead of /show), it displays the generated code.

showGSPs - they hook into the cache that stores the dynamically scaffolded pages.

listBasic
showBasic
Multiple Views -
While it was always possible to do multiple views, now

The above code inserts <flot:resources/> code fragment into the header area of the show page, and then redirects some data to a template. Note that you need to know that inside show page scaffolding, the instance of current object is called "weatherStationInstance".

In addition to the ScaffoldingHelper here, some scaffolding areas are defined inside WeatherStation class, inside constraints properties. This is done for convenience only, and can be defined in the code block above as well.

Maintaining Your Domain Objects in a Separate Plugin

The only gotcha there is that if you choose to use contraints method of setting visibility of properties, there is a problem with dependency on the constants class.

The suggested workaround is to copy the Constants into your own constants. This way, if you want ot maintain your domain objects outside of the main grails project in a separate plugin, you have no coupling between domain object hierarchy and the plugin, and don't have an artificial dependency.

Plans

Plans are to provide a few extra extension points for scaffolding templates.
Possible integration / interoperability with Scaffoldtags plugin
Ability to define some extension areas inside properties files
Possibly a DSL
Allow to use generate-all (I think it may be broken, at least in some cases)
Have built in constructs (i.e. some constants) for including templates easier, or to swap out other pages / layouts very easily.
Make Joda-Time more optional

Conclusion

Importantly, if plugins were to designed to account for this technique, this would allow to ease consumption of those plugins. The infrastructure provided by UberScaffoldig plugin could be used to ease that integration. The example above already demonstrates how to integrate flot plugin, but plugins themselves could interact with ubsercaffolding to insert themselves wherever needed. Thus, the user would only have to specify a config entry to enable capabilities such as filtering, for example, or drill down, or others. The user of the plugins would have to do very little to take advantage of basic plugin functionality, further increasing the DRYness of Grails ecosystem.

Release History

0.1 (Sept 30, 2009) - Initial Release
0.1.1 (Oct 8, 2009) - Some minor changes, resolved issues with release-package so the plugin is now can be installed with grails install-plugin.