49
Grails Notes 1 file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM] Grails Version 2.4.3 Mocking Views Controllers Methods Testing Overview GPath Domain Objects Conventions Constraints/Validation Global Constraints Custom Validators Custom Mappings Controllers Purpose Conventions Scope Default action Logging Standard Request params Diff between Java and Grails Variable Scopes in Controller Accessing Request Parameters Request Parameter Type Conversions Rendering Text Redirecting a Request Redirect Arguments Creating a Model Rendering a View DataBinding Conventions Binding to one Domain Binding to Multiple Domain Controlled data binding Data Binding and associations - Rushi Desai Grails

Grails Notes

Embed Size (px)

DESCRIPTION

Some note about Grail. Tips on best practices of grails and groovy.

Citation preview

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    GrailsVersion 2.4.3Mocking

    ViewsControllers Methods

    Testing OverviewGPathDomain Objects

    ConventionsConstraints/Validation

    Global ConstraintsCustom ValidatorsCustom Mappings

    ControllersPurposeConventionsScopeDefault actionLoggingStandard Request paramsDiff between Java and GrailsVariable Scopes in ControllerAccessing Request ParametersRequest Parameter Type ConversionsRendering TextRedirecting a Request

    Redirect ArgumentsCreating a ModelRendering a ViewDataBinding

    ConventionsBinding to one DomainBinding to Multiple DomainControlled data bindingData Binding and associations

    - Rushi Desai

    Grails

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Bindable ConstraintValidating DataHandling Validation errors

    ControllersView

    Command ObjectsConventionDefining Command ObjectsBinding

    Restricting HTTP methodsMultiPart FilesInterceptors vs Filters

    AOP type interceptorsTesting Controllers

    ViewsConventionsSupportsTags

    VariableBranching

    , , and Looping

    g:eachg:while

    Filtering and iterationg:collectg:findAll

    Grails Dynamic TagsLinking Tags

    g:linkLinking resources

    g:createLinkg:resource

    Forms:g:form

    Input fields:g:textFieldg:checkBoxg:radioBoxg:select

    Fields Plugin:f:field, f:input, f:display, f:allg:hasErrors

    Paginating Viewsg:paginate

    Templating

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    g:renderMapping URLs

    Default URL MappingStatic text in URLRemoving Controller and Action namesEmbedding ParametersPassing Additional params:Mapping to direct viewConstraintsWildcards

    * Wildcard** WildcardWildcard variable

    HTTP Request MethodsMapping HTTP Response CodesNamed URL Mappings

    Option1:Option2: with namespace

    Custom URL Mapping classesTest Customer URL Mapping

    InternationalizationSelecting properties file

    Query ParameterURL Mappings

    Parameterized Messagesjava.text.MessageFormatUsing the message Tag for Parameterized MessagesgetMessage() in Controller

    Error and MessagesError codes and Classes

    My Two CentsGeneralController

    Error Handling in ControllerServiceViewsDomainGORMTagLibTestingConfig.groovyPluginsSome Other Tips

    Appendix:Domain Classes Used

    UserDomain

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    StoreDomain:AlbumDomainSongDomainArtistDomain

    http://grails.org/doc/2.4.x/guide/upgradingFrom23.html

    dynamic scaffolding:static scaffold = true

    static scaffolding:

    grails generate-controller: Generates a controller for the specified domain class.grails generate-views: Generates views for the specified domain class

    grails generate-all: Generates both a controller and associated views.

    generate views and controller from IDE:

    From domain by right clicking any domain class and selecting generate controller andviews.

    This provides some default views and methods.

    list.gsp : Used by the list action to display a list of domain instances.

    show.gsp : Used by the show action to display an individual domain instance.

    edit.gsp : Used by the edit action to edit a domain instance properties.

    create.gsp : Used by the create action to create a new domain instance.

    _form.gsp : Used by the create and edit views.

    def index()def show()def delete()def edit()def update()

    Version 2.4.3

    Mocking

    Views

    Controllers Methods

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    def create()def save()def notFound()

    Grails separates tests into unit and integration tests.Integration tests bootstrap the whole environment, including the database; hence, they tend torun more slowly. In addition, integration tests are typically designed to test the interaction of anumber of classes and therefore require a more complete application before you can run them.Unit tests, on the other hand, are fast-running tests, but they require extensive use of mocks andstubs. Stubs are classes used in testing that mimic the real behavior of methods by returningarbitrary hard-coded values.The test-app command will execute all the tests (Both unit and Integration tests) in theapplication and output the results to the test/reports directory.

    .. that returns the parent* that returns all the children** that act as a depth first loop@ that is used to access a propertythe normal node accessor.

    By default, all the fields in a domain class are persisted to the database.If we want a particular field not to be persisted there are two approaches:The behavior in Grails 2.3.x is such that constrained properties in command objects and otherclasses marked with @Validateable are all configured with nullable: false by default.Unconstrained properties were not configured with nullable: false. In Grails 2.4 all non-staticunconstrained properties in command object classes and other classes marked with@Validateable are all configured with nullable: false.

    Examples:

    class Company {

    Testing Overview

    GPath

    Domain Objects

    Conventions

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Approach 1 :

    Approach 2 : to define it as a derived property. Derived properties are read-only persistentproperties whose values are derived by the database at the time of retrieval. Because the valuesare derived, there is no corresponding column in the database to store the value. Instead, aformula in the form of an SQL expression, which represents ow the database should derive thevalue, must be provided.

    For simple field types such as :Strings and Integers, each field in the class will map to a column in the database.Complex properties might require multiple tables to persist all the data.

    The table in the database will contain a separate column for each of those properties.id column, and a version column. The id is a unique identifier for a row, and Grails uses theversion column to implement optimistic locking.

    Table 3-1. Standard Validators in Grails

    BigDecimal cash

    BigDecimal receivables

    BigDecimal capital

    BigDecimal netWorth

    static mapping = {

    netWorth formula: 'CASH + RECEIVABLES + CAPITAL'

    }

    }

    static transients = ['salaryPaidYTD']

    static mapping = {

    netWorth formula: 'CASH + RECEIVABLES + CAPITAL'

    }

    Name Example Description

    blank login(blank:false) Set to false if a string value cannot be blank

    creditCard cardNumber(creditCard:true)Set to true if the value must be a credit-cardnumber

    email homeEmail(email:true)Set to true if the value must be an e-mailaddress

    inList login(inList:[Joe, Fred]) Value must be contained within the given list

    Constraints/Validation

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    min duration(min:1) Sets the minimum value

    mnSizei children(minSize:5)Sets the minimum size of a collection ornumber property

    matches login(matches:/[a-zA-Z]/) Matches the supplied regular expression

    max age(max:99) Sets the maximum value

    maxSize children(maxSize:25)Sets the maximum size of a collection ornumber property

    notEqual login(notEqual:Bob) Must not equal the specified value

    nullable age(nullable:false) Set to false if the property value cannot be null

    range age(range:16..59) Set to a Groovy range of valid values

    scale salary(scale:2)Set to the desired scale for floating-pointnumbers

    size children(size:5..15)Uses a range to restrict the size of a collectionor number

    unique login(unique:true) Set to true if the property must be unique

    url homePage(url:true) Set to true if a string value is a URL address

    grails.gorm.default.constraints = {

    '*'(nullable: true, size: 1..20)

    }

    ```+

    ###Validation using above constraints

    The __*save()*__ method on a domain object will automatically validate against the constraints

    before data are written to the database. Data are not written to the database if validation fails.

    If validation fails, the __*save()*__ method returns null. If validation passes, then the object

    is saved to the database, and the __*save()*__ method returns a reference to the object saved.

    e.g.

    ```java

    def song = new Song(title:'The Rover',artist:'Led Zeppelin',duration:-68)

    if(song.save()) {

    println "Song was created!"

    } else {

    Global Constraints

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    An alternative approach is to invoke the save method with a failOnError parameter set to true,which will cause the save() method to throw a grails.validation.ValidationException ifvalidation fails. That would look like song.save(failOnError: true).org.springframework.validation.Errors

    We can do validation on domain object itself without going to db:

    Grails provides a validate() method, which returns a Boolean value toindicate whether validation was successful. The semantics are exactly the same as in the example withthesave() method, except that the validate() method doesnt attempt to save the instance to thedatabase.If validation fails, the application might want to make changes to the state of the domain object andmake another attempt at validation. All domain objects have a method called clearErrors(), whichclears any errors left over from a previous validation attempt.

    If a constraint is violated Grails will by convention look for a message code of the form:

    [Class Name].[Property Name].[Constraint Code]

    In the case of the blank constraint this would be user.login.blank so you would need a messagesuch as the following in your grails-app/i18n/messages.properties file:

    user.login.blank=Your login name must be specified!

    The class name is looked for both with and without a package, with the packaged version takingprecedence.

    e.g.

    song.errors.allErrors.each { println it.defaultMessage }

    }

    package org.springframework.validation

    interface Errors {

    List getAllErrors();

    int getErrorCount();

    FieldError getFieldError(String fieldName);

    int getFieldErrorCount();

    List getFieldErrors(String fieldName);

    Object getObjectName();

    boolean hasErrors();

    boolean hasFieldErrors(String fieldName);

    // ... remaining methods

    }

    def song = new Song(title:'The Rover',duration:339)

    if(!song.validate()) {

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    executing another controller action (possibly but not necessarily in the same controller)rendering a viewrendering information directly to the response

    song.clearErrors()

    song.artist = 'Led Zeppelin'

    song.validate()

    }

    class User {

    static constraints = {

    password(unique:true, length:5..15, validator:{val, obj ->

    if(val?.equalsIgnoreCase(obj.firstName)) {

    return false

    }

    })

    }

    }

    class Person {

    String firstName

    String lastName

    Integer age

    static mapping = {

    id column:'person_id'

    firstName column:'person_first_name'

    lastName column:'person_last_name'

    age column:'person_age'

    version false

    }

    }

    Custom Validators

    Custom Mappings

    Controllers

    Purpose

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    By default,URLs are mapped to controller actions by way of a convention. The first part of the URLrepresents which controller to access, and the second part of the URL represents which actionshould be executed.

    For example, /sample/first will execute the first action in the SampleController.Likewise,/sample/second will execute the second action in the SampleController.

    If no action is specified, then call is routed to default action.For example, /sample will route to default action in SampleController.(Which action isdefault action is discussed below.)

    A controller is prototyped by default, meaning that a new instance is created for each request,so developers dont need to be as cautious about maintaining thread-safe code in a singletoncontroller.The controllers scope may be defined by using a static property in the controller named scopeand assigning that property a value of singleton or session.To change the default scope for all controllers in an application, define a property in grails-app/conf/Config.groovy named grails.controller.defaultScope and assign it a value ofsingleton or session.

    1. static defaultAction = list2. index()3. If only one method in controller than it becomes the default method.

    log is automatically injected in all controller.

    Attribute Description

    actionName The name of the currently executing action

    actionUri The relative URI of the executing action

    controllerName The name of the currently executing controller

    controllerUri The URI of executing controller

    Conventions

    Scope

    Default action

    Logging

    Standard Request params

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    request : Objects placed into the request are kept for the duration of the

    currently executing request. (Results in completely stateless application : Scalability best)flash : Objects placed into flash are kept for the duration of the current

    request and the next request only.The flash object does still use the HttpSession instance internally tostore itself, so if you require any kind of session affinity orclustering, remember that it applies to the flash object, too.The flash object implements java.util.Map, so all the regular methods of this class are also available.

    session : Objects placed into the session are kept until the user session is

    invalidated, either manually or through expiration.

    servletContext : Objects placed into the servletContext are shared across

    the entire application and kept for the lifetime of the application.

    The servletContext is a rarely used scope that allows you to share state

    flash The object for working with flash scope

    log An org.apache.commons.logging.Log instance

    params A map of request parameters

    request The HttpServletRequest object

    response The HttpServletResponse object

    session The HttpSession object

    servletContext The ServletContext object

    Java Servlet Grails Controller

    request.getAttribute(myAttr); request.myAttr

    request.setAttribute(myAttr, myValue); request.myAttr = myValue

    session.getAttribute(mAttr); session.myAttr

    session.setAttribute(myAttr, myValue); session.myAttr = myValue

    servletContext.getAttribute(mAttr); servletContext.myAttr

    servletContext.setAttribute(myAttr, myValue); servletContext.myAttr = myValue

    Diff between Java and Grails

    Variable Scopes in Controller

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    across the entire application.

    If you really need to use the servletContext, you should prepopulate itwith any values you need at startup and then read those values only at runtime. This allowsyou to access the servletContext in an unsynchronized manner.

    Access to the servletContext object is not synchronized, so you need to do manual synchronization if you plan to read and write objects from the servletContextobject.e.g.

    request.getParameter(userName)params.userNamedef action(@RequestParameter(accountNumber) String mainNumber

    Whether you are using the RequestParameter annotation or associating method argumentswith request parameters by convention, if a corresponding request parameter does not existor a conversion error occurs for method action arguments, then the default value for thecorresponding type will be passed in as the argument value. For Booleans this is false, forall of the other seven primitive types this is zero (0), and for all reference types this is null.Controllers have an errors property that may contain errors related to type conversions ofrequest parameters.

    Incoming request parameters are typically strings.

    To convert following options are available:

    params.counter.toInteger()params.int(counter)def secondAction(int counter, String name) : By declaring typesof params grails will automatically convert.

    Following supported:

    Boolean, byte, char, short, int, long, float, and double

    def index() {

    synchronized(servletContext) {

    def myValue = servletContext.myAttr

    servletContext.myAttr = "changed"

    render myValue

    }

    }

    Accessing Request Parameters

    Request Parameter Type Conversions

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    The methods each accept one or two arguments. The first argument is the name of the request parameter that is to be converted, and the optional second argument is a defaultvalue that will be returned if a corresponding request parameter cannot be found or if an erroroccurs during the conversion. For more details see : Handling Validation errors

    render this text will be rendered back as part of the responserender text:Revolver, contentType:text/xml

    redirect action: second : Redirects to action second() in same controllerredirect action: list, controller: store

    Examples:

    We can reference above model in view by name : song

    Argument Name Description

    action The name of or a reference to the action to redirect to

    controller The name of the controller to redirect to

    id The id parameter to pass in the redirect

    params A map of parameters to pass

    uri A relative URI to redirect to

    url An absolute URL to redirect to

    class SongController {

    def show() {

    [ song: Song.get(params.id) ]

    }

    }

    Rendering Text

    Redirecting a Request

    Redirect Arguments

    Creating a Model

    Rendering a View

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Default view : If none is specified default view is fetched.

    In Above code : Following view is rendered: grails-app/views/song/show.gsp

    Custom View :

    render view: display, model: [ song: Song.get(params.id) ]

    Above will render grails-app/views/song/display.gspThe view is automatically scoped with the grails-app/views/song directory.

    render view:/common/song, model:[song: Song.get(params.id) ]+ By starting with a / character, you can reference any view within the grails-app/views directory. In the previous example, Grails will try to render a view at thelocation grails-app/views/common/song.gsp.- Rendering templates :

    render template: /common/song, model: [song: Song.get(params.id) ]+ The name of the template starts with an underscore by convention.

    Content Negotiation :

    respondsTo

    default data binding mechanism will bind all properties that are not static, not transient, and notdynamically typed.If a command objects type is a domain class and there is no id request parameter then null willbe passed into the controller action unless the HTTP request method is POST, in which case anew instance of the domain class will be created by invoking the domain class constructor. Forall of the cases where the domain class instance is non-null, data binding is only performed if theHTTP request method is POST, PUT or PATCH. See the Command Objects documentationfor more details.

    Trivial way:

    class AlbumController {

    def save() {

    def album = new Album()

    album.genre = params.genre

    album.title = params.title

    album.save()

    }

    }

    DataBinding

    Conventions

    Binding to one Domain

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Second option:

    params object in your controller is a map of name/value pairs. We canpass map to constructor.* CAUTION : The default data binding mechanism will bind all properties that are not static,not transient, and not dynamically typed. The features detailed so far can leave your webapplication open to URL attacks due to the automatic setting of properties from requestparameters. This is a common issue among frameworks that perform such conversion (includingRuby on Rails, Spring MVC, WebWork, and others). If you are developing a web application withheightened security in mind, you should use fine-grained control over data binding throughbindable constraint and/or the bindData method (each described later), along with strictervalidation.

    Third option (Recommended) :

    We can use . convention to put namespace and bind data to multiple domains.

    above code when submitted to server grails will try to look for class Album and within it a propertycalled title.

    In controller equivalent code:Below code will look for all the properties with namespace album and namespace artistand then bind it.

    bindData() : input params : object to which bind , params , [condition] , [namespace]

    def album = new Album(params)

    album.save()

    def album = Album.get(params.id)

    album.properties = params

    album.save()

    def album = new Album( params["album"] )

    def artist = new Artist( params["artist"] )

    Binding to Multiple Domain

    Controlled data binding

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    (optional)

    Example1:

    Example2:

    one to one :

    one to many :

    many to many :

    Binding follows convention as mentioned before. We can override this by :Below code will bind :

    group : Not bindable by default since its not typed.userName : Bindable by default since not static and transient.

    Not Bind:numberOfActiveGroups : Not bindable by default since transient.salary : Bindable by default but we constraint it to not bind.

    class AlbumController {

    def save() {

    def album = Album.get(params.id)

    bindData(album, params, [include:"title"])

    // ...

    }

    }

    bindData(album, params, [include:title], album)

    class User {

    /* userName and salary would be bindable by default */

    String userName

    BigDecimal salary

    /* group and numberOfActiveGroups would not be bindable by default */

    def group

    transient int numberOfActiveGroups

    static constraints = {

    salary bindable: false

    group bindable: true

    }

    Data Binding and associations

    Bindable Constraint

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Combined with third approach above for binding we can validate following way:

    album.errors.allErrors.each { println it.code }if(album.hasErrors()) println Something went wrong!

    Example1:

    Example2:

    }

    def album = Album.get(params.id)

    album.properties = params

    album.validate() //This wont throw any exception. It will just populate error object. We

    need to manually check error and take appropriate decision.

    album.save()

    class AdminController {

    def action(String accountNumber, int accountType) {

    //All controllers have an errors object. Here errors will be populated if any happened

    during binding.

    if(accountNumber == null && errors.hasErrors()) {

    def accountNumberError = errors.getFieldError('accountNumber')

    if(accountNumberError != null) {

    // accountNumberError is an instance of

    // org.springframework.validation.FieldError

    // ...

    }

    }

    }

    }

    Validating Data

    Handling Validation errors

    Controllers

    View

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Purpose: Sometimes a particular action doesnt require the involvement of adomain class but still requires the validation of user input. In this case, youmight want to consider using a command object. A command object is a classthat has all the data-binding and data-validation capabilities of a domain classbut is not persistent.

    All command objects have suffix Command.

    e.g. AlbumCreateCommand that encapsulates the validation and creation ofnew Album instances before they are saved.

    In order to use a command object, you need to specify the command as the first argument in acontroller action.

    Examples:

    CommandObject:

    def save() {

    def album = Album.get(params.id)

    album.properties = params

    if(album.save()) {

    redirect action: "show", id: album.id

    } else {

    render view: "edit", model: [album:album]

    }

    }

    class AlbumCreateCommand {

    String artist

    String title

    List songs = []

    List durations = []

    static constraints = {

    artist blank:false

    title blank:false

    songs minSize:1, validator:{ val, obj ->

    if(val.size() != obj.durations.size())

    return "songs.durations.not.equal.size"

    }

    Command Objects

    Convention

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    (See: Album, Song)

    Controller

    View:

    Render Errors in view:

    }

    //Optional, allows to create album instance after validation of data.

    //Command object as factories that take input data, validate and give instance.

    Album createAlbum() {

    def artist = Artist.findByName(artist) ?: new Artist(name:artist)

    def album = new Album(title:title)

    songs.eachWithIndex { songTitle, i ->

    album.addToSongs(title:songTitle, duration:durations[i])

    }

    return album

    }

    }

    class AlbumController {

    def save(AlbumCreateCommand cmd) {

    if(cmd.validate()) {

    def album = cmd.createAlbum()

    album.save()

    redirect(action:"show", id:album.id)

    }

    else {

    render(view:"create", model:[cmd:cmd])

    }

    }

    }

    Title:

    Artist:

    Song 1:

    Song 2:

    ...

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    A command object requires the definition of class, just as with any other object. One can definecommand classes in the grails-app/controllers directory or even in the same file as a controller.Unlike Java, Groovy supports the notion of multiple class definitions per file, which is quitehandy if you plan to use a particular command object only for the controller youre working with.

    (Only in grails 2.4 and above) Request parameter names may now be prefixed with the name

    of the controller action argument name that the request parameter should be bound to. Forexample, if a request is made to the buy action in the controller below a request parameternamed buyer.name will be bound to the name property of the buyer argument and a requestparameter named seller.name will be bound to the name property of the seller argument.

    1. Branching:

    1. Declarative syntax: allowedMethods

    NOTE: If the rules expressed in the allowedMethods property are violated, the framework will denythe request and return a 405 error code, which the HTTP specification defines as Method NotAllowed

    Use interceptors when one wants to intercept some particular actions within one controller.If one wants to intercept calls for multiple controllers use filters.

    (request.method == "GET") {}

    class SomeController {

    // action1 may be invoked via a POST

    // action2 has no restrictions

    // action3 may be invoked via a POST or DELETE

    static allowedMethods = [action1: 'POST', action3: ['POST', 'DELETE']]

    def action1() { ... }

    def action2() { ... }

    def action3() { ... }

    }

    Defining Command Objects

    Binding

    Restricting HTTP methods

    MultiPart Files

    Interceptors vs Filters

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Before:

    Option1:

    Option2: More fine grain control

    After: (Same as before)

    Unit Testing by default doesnt load the whole env. Refer Testing Overview

    Samples:

    Controller:

    (See: Album)

    //By default this will apply to all actions in controller.

    def beforeInterceptor = {

    log.trace("Executing action $actionName with params $params")

    }

    class AlbumController {

    private trackCountry = {

    //

    }

    //Will apply 'trackCountry()' method, only to 'show()' action.

    def beforeInterceptor = [action: trackCountry, only: 'show']

    }

    //By default this will apply to all actions in controller.

    def afterInterceptor = {model ->

    log.trace("Executed $actionName which resulted in model: $model")

    }

    class AlbumController {

    def list() {

    [albumList: Album.list()]

    }

    }

    AOP type interceptors

    Testing Controllers

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Testing Controller that returns a model: (See : [Album][albumLink])

    (See: Album)

    Testing controller that return test:

    Some other examples of assertion:

    import grails.test.mixin.*

    @TestFor(AlbumController)

    @Mock(Album)

    class AlbumControllerTests {

    void testListAction() {

    //We can call .save() on Album instance because of @Mock(Album) mixin.

    //Else in Unit test env gorm methods arent injected.

    new Album(title: 'Trilogy').save()

    new Album(title: 'Tarkus').save()

    //We can use 'controller' because of @TestFor(AlbumController).

    //This automatically inserts above field since argument passed is a controller.

    def model = controller.list()

    assert model.albumList?.size() == 2

    }

    }

    @TestFor(StoreController)

    class StoreControllerTests {

    void testSomething() {

    //'controller' comes because of @TestFor

    controller.index()

    //'response' comes because of @TestFor

    //Here 'response' is not regular servlet HttpServletResponse

    //It is a Grails MockHttpServletResponse

    assert 'Welcome to the gTunes store!' == response.text

    }

    }

    //Assert model has errors

    assert cmdModel.hasErrors()

    assert 'user.not.found' == cmd.errors['login'].code

    //Assert model is returned.

    assert session.user == null

    //Assert view

    assert '/store/index' == view

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    1. Similar to JSP (can write complete JSP code, supports all JSP code) but more powerful.2. Supports Groovy featuressuch as regular expression support, GStrings, and an expressive

    syntax for maps and lists.3. GSP tags are, at root, just closures.4. GSP attributes can be specified as maps with the [key:value] syntax.

    Groovy imports the java.lang, java.util, java.io, java.net, groovy.lang, and groovy.util packages bydefault.

    GPath

    Groovy Scriptlets

    GStrings

    (See: Album)

    assert session.user != null

    assert '/store' == response.redirectedUrl

    I'm printed three times!

    Views

    Conventions

    Supports

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    application: Stores variables for the scope of the whole applicationsession: Stores variables for the scope of the user sessionflash: Stores variables for the current request and the next request onlyrequest: Stores variables for the scope of the current requestpage: Stores variables for the scope of the rendering page

    (See: Album)

    (See: Album)

    Delegates to each method.In Groovy the variable it refers to the default argument of the innermost closure.If one uses the tag without declaring a var attribute and try to reference the default it variablewithin a nested GSP tag, this will result in evaluating it to the current innermost tag and not thesurrounding tag. By naming the variable used by using the var attribute, one can circumvent thisconflict and any similar ones.

    Classic rock

    Modern Rock

    Other

    Tags

    Variable

    Branching

    , , and

    Looping

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Examples:All below examples get the titles of all albums:

    (See: Album, Song)

    (See: Album, Song)

    We might need to iterate over collection and only get some properties:

    Its groovys collect() and each() used together.

    ${song.title}

    ${album.songs[i].title}

    ${it}

    ${it}

    g:each

    g:while

    Filtering and iteration

    g:collect

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    (See: Album)

    Using GPath expression:

    (See: Album)

    Collecting properties from a collection via the object graph was achieved above, but one mightalso want to iterate over only those values that meet certain criteria. We can do it by g:findAlltag.

    (See: Album)

    The default argument it is the current Album instance being iterated over.It uses GPath to retrieve a collection of all the names of the songs.The songs property, too, is itself a collection (a java.util.Set, to be specific) and does not have atitle property, but GPath recognizes that the reference to the title property is an attempt toretrieve a collection of name properties from the contained elements within the songs property.Since the result is a collection, you can invoke the regular JDK contains method to look up allalbums that have the word Love in their title. The result, far more readable than a bunch ofnested if statements, is another case where you can see how a Groovy view technology like GSPmakes a remarkable amount of sense.

    Found under: grails-app/taglibNo import required to use them. (And hence dynamic)Dynamic tags can also be invoked as methods from scriptlets and GStringexpressions: Useful to maintain a clean syntax and valid XML

    *Example: *

    JSP:

    ${it}

    ${it.title}

    g:findAll

    Grails Dynamic Tags

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Equivalent GSP:

    Can change the css class of the tag after looking at the data. e.g.Can give error css class if model given from controller has errorCan give a regular css class if model given from controller is error free.

    Examples:

    The tag will essentially create a simple HTML anchor tag based on the supplied attributes, whichinclude the following.

    controller: the controller name to link toaction: the action name to link toid: the identifier to append to the end of the URImapping: the name of the URL mapping to useparams: any parameters to pass as a map

    One of either the controller or the action attribute is required. If the controller attribute isspecified but no action attribute is specified, the tag will link to the default action of thecontroller. If, on the other hand, an action attribute is specified but no controller attribute isspecified, the currently executing controller will be linked to.tag also supports all attributes that the regular HTMLanchor tag supports, which can be added as required.

    Examples:

    Parameters as maps: We can pass parameters to link.

    A dynamic link

    A dynamic link

    A dynamic link

    list Albums

    Show album with id K/g:link>

    Linking Tags

    g:link

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Instead of specifying a map explicitly, you provide a reference to the params object via the ${}expression syntax, which then allows parameters to be passed from the current page to the linkedpage.

    takes the same arguments as the tag, except that it produces just the textual link and not anHTML anchor tag. - In fact, the tag actually delegates to when creating its href attribute.

    Examples:

    Allows convenient linking to resources within the web applications context path.This tag is most commonly used for linking to images and style sheets

    Examples:

    NOTE: Both tags tend to be used via method calls as opposed to markup, because the valuesproduced by them are usually nested within attributes of other tags.

    Same arguments as those shown with the tag to allow easy submission to a specific controller oraction or both.By default, the tag uses the POST method for form submissions.

    Examples:

    Show first ten ordered

    by Title

    Pass parameters from this action to next

    List Albums

    var listAlbumsLink = "${createLink(action:'list')}";

    Linking resources

    g:createLink

    g:resource

    Forms:

    g:form

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Examples:

    Can give any boolean expression to decide whether to render the checkbox as .checked orunchecked

    Examples:

    Same as checkbox can give some boolean expression.

    Examples:

    from: to give range of values.value: To tell which value to be rendered as default selected value.

    Examples:

    Radio 1

    Radio 2

    Input fields:

    g:textField

    g:checkBox

    g:radioBox

    g:select

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    To render domain fields in GSP, one approach is to manually create all the html/gsp.

    Examples:

    This is :repetitive : We have to repeat this for all domain objectsMaintenance nightmare : If we change a domain we need to change UI.

    Refer: Plugin Documentation

    Rock

    Blues

    Jazz

    Undertow

    *

    Fields Plugin:

    f:field, f:input, f:display, f:all

    g:hasErrors

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Supports the following attributes:

    bean: a bean instance to inspect for errorsfield: the name of the field to check for errorsmodel: an alternative to specifying a bean; an entire model (map) can be checked

    Supports method based call as well as tag based calls.

    Examples:

    Checks whether there are any errors for any bean throughout the request scope:

    Checks whether there are any errors for the specified bean

    Checks whether there are any errors for the field title of the specified book bean:

    Method call:

    Attributes:

    Arguments Description

    total Total number of elements in the larger list

    controller Name of the controller to link to

    Paginating Views

    g:paginate

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    All of the parameters supported by the paginate tag are optional except for the total parameter.

    Can breakup the page into more modular smaller pages and combine those small pages into onepage.Allows code reuse and better code maintenance.Header, Footer are good candidates to be used as templates.GSP templates must be defined in a file whose name begins with an underscore.Directory : grails-app/views/album/Useful for writing Ajax-driven behavior.Can pass model to template.

    Examples:

    Rendering the albumList Template:

    artistList template:

    action Name of the action to invoke

    params Map of request parameters

    offset Offset to be used if params. Offset is not specified

    max Maximum number of elements per page

    prev Text for the Previous link

    next Text for the Next link

    id ID to use in links

    maxsteps Number of steps displayed for pagination (the default is 10)

    ${artist?.name}

    Templating

    g:render

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Passing model to template:

    /$controller/$action?/$id?the $action and $id elements are both followed by a question mark. The question mark indicatesan optional piece of the URL. The $controller element has no question mark, so it is a requiredpiece of the URL.

    Maps all urls patterns like :showAlbum/XshowAlbum/X/YshowAlbum/X/Y/Z

    Latest Albums

    Latest Songs

    Newest Artists

    Mapping URLs

    Default URL Mapping

    Static text in URL

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Option 1:

    Option 2:

    By default URL params are accepted

    /showArtist?artistName=Rush is supported by default mapping

    Other way:

    class UrlMappings {

    static mappings = {

    "/showAlbum/$controller/$action?/$id?" {

    constraints {

    // apply constraints here

    }

    }

    }

    }

    class UrlMappings {

    static mappings = {

    "/showAlbum/$id" {

    controller = 'album'

    action = 'show'

    }

    // ...

    }

    }

    class UrlMappings {

    static mappings = {

    "/showAlbum/$id"(controller:'album', action:'show')

    // ...

    }

    }

    class UrlMappings {

    static mappings = {

    Removing Controller and Action names

    Embedding Parameters

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Controller Code to access URL params:

    Url with spaces will have %20 which can cause problems.Work around is to constraint the url params to not have spaces and be delimited by _ orwhichever is comfortable and replace in controller by spaces

    "/showArtist/$artistName"(controller:'artist', action:'show')

    // ...

    }

    }

    class ArtistController {

    def show() {

    def artist = Artist.findByName(params.artistName)

    // do whatever is appropriate with the artist...

    }

    }

    class UrlMappings {

    static mappings = {

    "/showArtist/$artistName"(controller:'artist', action:'show') {

    format = 'simple'

    }

    "/showArtistDetail/$artistName"(controller:'artist', action:'show') {

    format = 'detailed'

    }

    // ...

    }

    }

    class UrlMappings {

    static mappings = {

    "/"(view:'/welcome')

    // ...

    }

    }

    Passing Additional params:

    Mapping to direct view

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Will mathc URL patterns like:

    /grailsblogs/2009/01/15/new_grails_release/grailsblogs/2009/01/15/12

    Will NOT match URL pattern like:

    /grailsblogs/hi/01/15/new_grails_release/grailsblogs/2009/hi/15/new_grails_release/grailsblogs/2009/01/hi/new_grails_release

    A double wildcard can be used to match any number of subdirectories.

    class UrlMappings {

    static mappings = {

    "/grailsblogs/$year/$month/$day/$entry_name?" {

    controller = 'blog'

    action = 'display'

    constraints {

    year matches: /[0-9]{4}/

    month matches: /[0-9]{2}/

    day matches: /[0-9]{2}/

    }

    }

    }

    }

    class UrlMappings {

    static mappings = {

    "/images/*.jpg"(controller:'image')

    }

    }

    Constraints

    Wildcards

    * Wildcard

    ** Wildcard

    Wildcard variable

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Option 1:

    Option 2:

    The default mapping includes a mapping for the 500 response code (Internal Error).2 Thismapping renders the /error view for any internal error. This view is located at grails-app/views/error.gsp.

    class UrlMappings {

    static mappings = {

    "/images/$pathToFile**.jpg"(controller:'image')

    }

    }

    class ArtistController {

    def actionName() {

    if(request.method == "GET") {

    // handle the GET

    } else if(request.method == "PUT") {

    // handle the PUT

    } else if(request.method == "POST") {

    // handle the POST

    } else if(request.method == "DELETE") {

    // handle the DELETE

    }

    }

    }

    class UrlMappings {

    static mappings = {

    "/artist/$artistName" {

    controller = 'artist'

    action = [GET: 'show',

    PUT: 'update',

    POST: 'save',

    DELETE: 'delete']

    }

    }

    }

    HTTP Request Methods

    Mapping HTTP Response Codes

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    There may be scenarios where multiple URL mappings match a given request for a reversemapping and the framework wont necessarily know which mapping to apply. Named URLmappings solve this problem by allowing a mapping to have a name associated with it and thenreferring to that name when requesting a reverse URL mapping lookup.

    class UrlMappings {

    static mappings = {

    // 404s should be handled by the default

    //action in the store controller

    "404"(controller:'store')

    // IllegalArgumentExceptions should be handled by the

    // illegalArgument action in the errors controller

    "500"(controller: "errors", action: "illegalArgument",

    exception: IllegalArgumentException)

    // NullPointerException should be handled by the

    // nullPointer action in the errors controller

    "500"(controller: "errors", action: "nullPointer",

    exception: NullPointerException)

    // MyException should be handled by the

    // customException action in the errors controller

    "500"(controller: "errors", action: "customException",

    exception: MyException)

    // all other exceptions should be handled by

    // the /errors/serverError view

    "500"(view: "/errors/serverError")

    // ...

    }

    }

    class UrlMappings {

    static mappings = {

    name artistDetails: "/showArtist/$artistName" {

    controller = "artist"

    action = "show"

    }

    }

    }

    Named URL Mappings

    Option1:

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    The link tag supports an optional attribute named mapping, which may be used to specify thename of the mapping that should be used for this lookup.

    Notice that there is no need to specify the controller or action name.

    Below is equivalent to above code.Note the namespace artistDetails used with

    When an application defines a lot of custom URL mappings, the UrlMappings class may get longenough to warrant breaking the mappings up into several mappings classes. Several small,focused mappings classes will be easier to write and maintain than one monolithic class.To introduce new mappings classes, simply define classes under grails-app/conf/ with a namethat ends with UrlMappings.

    ${artist.name}

    ${artist.name}

    class ArtistUrlMappings {

    static mappings = {

    "/showArtist/$artistName" (controller:'artist', action:'display')

    }

    }

    @TestFor(ArtistUrlMappings)

    @Mock(com.gtunes.ArtistController)

    class ArtistUrlMappingsTests {

    void testShowArtistUrlMapping() {

    // assert that /showArtist/Jeff_Beck is handled by the

    // display action in the artist controller and a request

    // parameter named artistName exists with the value Jeff_Beck

    Option2: with namespace

    Custom URL Mapping classes

    Test Customer URL Mapping

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Controller:

    View:

    ?lang=es

    Will map/store/es to spanish/store/en to english etc.

    assertForwardUrlMapping('/showArtist/Jeff_Beck',

    controller: 'artist', action: 'display') {

    artistName = 'Jeff_Beck'

    }

    }

    }

    // GroovyMessages.groovy

    def messages = ResourceBundle.getBundle('messages')

    def appName = messages.getString('app.name')

    println "application name is ${appName}"

    class UrlMappings {

    static mappings = {

    "/store/$lang"(controller:'store')

    // ...

    }

    }

    Internationalization

    Selecting properties file

    Query Parameter

    URL Mappings

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    grails-app/i18n/messages.properties

    Grails provides a bean named messageSource that can be injected into any Grails artefact{Throughout the source code and documentation of Grails, the word artefact is used to refer toa Groovy file that fulfills a certain concept (such as a controller, tag library, or domain class).It is spelled using the British English spelling of artefact (as opposed to artifact)}, includingcontrollers, taglibs, other beans, and so on. The messageSource bean is an instance of theorg.springframework.context.MessageSource interface provided by the Spring Framework. Thisinterface defines three overloaded versions of the getMessage method for retrieving messagesfrom the source.

    Usage:

    # messages.properties

    gtunes.purchased.songs=You have purchased ({0}) songs.

    import java.text.MessageFormat

    def bundle = ResourceBundle.getBundle('messages')

    def songsPurchased = bundle.getString('gtunes.purchased.songs')

    def message = MessageFormat.format(songsPurchased, 97)

    println "message: ${message}"

    String getMessage(String code, Object[] args, Locale locale)

    String getMessage(String code, Object[] args, String defaultMessage, Locale locale)

    String getMessage(MessageSourceResolvable resolvable, Locale locale)

    def msg = messageSource.getMessage('gtunes.my.music', null, null)

    def msg = messageSource.getMessage('gtunes.my.music',null,Locale.ITALIAN)

    def msg = g.message(code:'gtunes.my.music')

    Parameterized Messages

    java.text.MessageFormat

    Using the message Tag for Parameterized Messages

    getMessage() in Controller

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    As you can see above we use some error class to test if error happened or not. Below are someclasses we can check for:

    ${flash.message}

    Validator Error Code(s)

    blank className.propertyName.blank

    creditCard className.propertyName.creditCard.invalid

    email className.propertyName.email.invalid

    inList className.propertyName.not.inList

    min className.propertyName.min.notmet

    minSize className.propertyName.minSize.notmet

    matches className.propertyName.matches.invalid

    max className.propertyName.max.exceeded

    maxSize className.propertyName.maxSize.exceeded

    notEqual className.propertyName.notEqual

    Error and Messages

    Error codes and Classes

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Generally push everything down to the lowest level possible this simplifies all the levels above.Dont do in a view what you can do in a controller.Dont do in a controller what you can do in a service.Dont do in a service what you can do in a domain class.

    Dont allow the controller to take over another role. The role of a controller is to accept incomingrequests, check permissions etc, ask a domain or a service for a result, give the result back to therequester in the desired format such as HTML, JSON, or XML. Keep the controller as thin aspossible. Dont perform business logic, queries, or updates within controllers.

    If a controller represents a single domain class, use the standard naming convention ofController. Avoid code duplication - common operations should be extracted as a closure or amethod. See this blog.

    Split complex data binding into a command object. You can make command objects rich (justlike rich domain classes). Creating a hierarchy of command objects can also be useful in somescenarios.

    Define and (usually) type input variables in method signature.

    Just because you dont HAVE to catch exceptions, doesnt mean you SHOULDNTCommon example is to redirect everything to a common error pagetry/catch block around probably all service calls especially ones that update data

    nullable className.propertyName.nullable

    range className.propertyName.range.toosmall

    className.propertyName.range.toobig

    size className.propertyName.size.toosmall

    className.propertyName.size.toobig

    unique className.propertyName.unique

    url className.propertyName.url.invalid

    My Two Cents

    General

    Controller

    Error Handling in Controller

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Let exceptions influence navigationInfo and error messages in flash. (Although I would want to avoid the use of flash completely,since using flash scope impacts scalability of application. It makes the server statefull.)

    A service is the right candidate for complex business logic or coarse grained code. If required,the service API can easily be exposed as a RESTful/SOAP web service. Services are transactionalby default, but can be made non-transactional if none of their methods update the persistencestore.

    Statelessness: Services should be stateless. Use domain object for state full operation.

    Keep views as simple as possible - avoid the temptation to put business or database logic in thislayer.Use layouts to ensure a consistent look across all, or a sub-set of, the applications pages.Keep your views DRY (Dont Repeat Yourself). Split the repeated content into templates.Use custom TagLibs for common UI elements.

    Favor placing model domain specific logic in its own domain. Anything that applies to a singledomain with few dependencies should go in its domain class.But keep it restricted to the logicspecific to that domain only - more complex business logic that deals with a group of domainsbelongs to a service.

    To reuse common partial queries or decompose the complex logic, use named queries and chainthem together as required, just like one commonly chains jQuery function calls.

    Dont mix any other common utility classes or value objects in the domain folder, rather they cango in src/groovy. If these classes need to support validation, one can annotate them with@Validateable.

    Use sensible constructors for instantiating domain objects, to avoid any unwanted state and toconstruct only valid objects.

    Dont do cross-domain work : Except for some simple managing of existing relationships

    Dont inject services into domainsDont expose internal variables

    Name these methods retrieve*()

    Distinguishes them from dynamic find*() methods

    Service

    Views

    Domain

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    Util methods to navigate complicated relationships

    Loose coupling of calling code

    Named Queries

    All above do the same. They give same result.But the difference is in testability.

    Keep an individual tag light. A tag can call other tags, and it is acceptable to break a tag intoreusable sub-tags if required.

    The TagLib is considered part of the view layer in the MVC architecture, but it is acceptable todig into the domain as required to assemble or format the data for display. Still follow theapproach to minimize (i.e not to blanket ban) direct interaction with the domain.

    def retrieveSubObjsForType( type ) {

    Domain.withCriteria {

    eq( "domain.id", this.id )

    collectionOfSubObjs {

    eq( "subObj.id", type.id )

    }

    }

    }

    boolean hasCertainAssociations() {

    this.collection?.any {

    it.otherCollection?.size() > 0

    }

    }

    def getAssociationCount() {

    collection*.subObjs?.sum { it.size() }

    }

    Person.findByFirstName(John)

    Person.where { firstName == John }.find()

    Person.createCriteria { eq (firstName, John) }.find()

    new DetachedCriteria(Person).build { eq firstName, John }.find()

    Person.find(from Person p where p.firstName = John)

    GORM

    TagLib

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    It should contain more of logic than rendering; although a little bit of rendering is fine.

    Use multiple custom taglibs for better organization.

    Favor units tests over integration tests. As well as being faster to run/debug they enforce loosecoupling better. An exception is for service testing, where integration testing is generally moreuseful.In unit tests, use save(validate:false) to save objects which are not completely loaded.

    Place all environment specific settings in Config.groovy, such as serverURL, constants whichdiffer per environment, etc.

    Keep personal settings (such as local database username or passwords, etc) in a{Local}Config.groovy file and add to version control ignore list, so that each team member canoverride configuration as per their specific needs.Somewhat contentious, but Id advise setting grails.gorm.failOnError = true so that an exceptionis thrown as soon as domain validation fails while saving an object. Given this, you no longerneed to check whether the save was successful. I have had to go through crap to figure out iferror happened or not everytime.

    In Grails 2.0 and later grails.hibernate.cache.queries = true by default, which caches queriesautomatically without a need to add cache:true. Set it to false, and cache only when it genuinelyhelps performance.

    Refer here

    Understand and stick to Grails conventions, as Grails is convention driven.

    Using these conventions will make life easier for you as a developer.

    To organize Grails artifacts in different packages, dont do thiscom.businessname.appname.domain and com.businessname.appname.controller. Otherwisebeing in the FooController, we would end up importing Foo class. Since Grails already keepsthese artifacts in different folders, they dont need to be separated further.

    The fixtures plugin may be used to bootstrap your data during development.

    Develop re-usable parts of your application as Grails plugins. These plugins can be tested

    Testing

    Config.groovy

    Plugins

    Some Other Tips

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    individually and will remove complexity from your main application(s) using them. Considerpublishing the plugins in the public plugin repository if you think others can benefit from them.

    Update the scaffolded templates to generate your project specific views & controllers.

    Prefer dynamic scaffolding to static scaffolding until the former no longer satisfies yourrequirements. For example, if only save action needs to be modified, you can override just thatsave action and generate scaffolded code dynamically at runtime.

    Its good to always provide database re-connection properties in DataSource.groovy.

    Always ensure that you include an externalized config file (even if its an empty file), so that anyconfiguration that needs to be overridden on production can be done without even generating anew war file.

    If you need to make a small change to the plugin you are using, for example change list.gsp of thequartz monitor plugin to go with your application theme, then instead of making the plugininline for this small change, you can override these files by following the same directorystructure or package. This works since the application gets higher priority over the plugins used.

    All custom validators of the domain can be put in a shared validators file, to support re-usabilityof these constraints amongst other domains. See here.

    To install any plugin in your application, its better to declare it in BuildConfig.groovy ratherthan using the install-plugin command. Read this.

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    class User {

    String login

    String password

    String firstName

    String lastName

    static hasMany = [purchasedSongs:Song]

    static constraints = {

    login blank:false, size:5..15,matches:/[\S]+/, unique:true

    password blank:false, size:5..15,matches:/[\S]+/

    firstName blank:false

    lastName blank:false

    }

    }

    class Album {

    String title

    static hasMany = [songs:Song] //Will generate 'song' Variable of type Set

    static belongsTo = [artist:Artist]

    //Overriding the default type of Song which is Set

    SortedSet songs

    }

    class Song {

    String title

    Appendix:

    Domain Classes Used

    UserDomain

    StoreDomain:

    AlbumDomain

    SongDomain

  • Grails Notes 1

    file:///C|/Users/desair1/AppData/Local/Temp/30.html[8/17/2014 4:44:29 PM]

    String artist

    Album album

    Integer duration

    static belongsTo = Album

    static constraints = {

    title blank: false

    artist blank: false

    }

    }

    class Artist {

    String name

    static hasMany = [albums:Album, instruments:Instrument]

    }

    ArtistDomain

    Local DiskGrails Notes 1