Tuesday, February 21, 2012

Searching Multiple Data Sources in Grails

Have you got domain classes mapped to multiple datasources? The Searchable plugin will mysteriously not be building indexes for domain classes in your secondary datasources by default. The following explains a way to change the behavior.

This is taken from my personal experience, and information compiled from a helpful thread archived on Grails, found here. We are going to need to redefine some beans.

Step 1:
Open conf/spring/resources.groovy


add in the following bean:


import org.compass.gps.device.hibernate.HibernateGpsDevice //need this import for new datasource
import grails.plugin.searchable.internal.compass.config.SessionFactoryLookup //need this import to get correct sessionfactory class


beans = {


/* ~ your other beans here */



compassGpsDevice(HibernateGpsDevice) { bean ->
bean.destroyMethod = "stop"
name = "hibernate"
sessionFactory = { SessionFactoryLookup sfl ->
sessionFactory = ref('sessionFactory_datasourceName') 
}//replace datasourceName with the name of the datasource as defined in conf/DataSource.groovy
fetchCount = 5000
}
}


Step 2:
You could stop here if this was the only datasource that contained indexable classes, but continue to add each compassGPSdevices for each additional datasource (including the default), making sure they are unique names ie:



anotherUniquecompassGpsDevice(HibernateGpsDevice) { bean ->
bean.destroyMethod = "stop"
name = "unqiueHibernateName"
{ SessionFactoryLookup sfl ->
sessionFactory = ref('sessionFactory_uniquedatasource') 
}
fetchCount = 5000
 }


then finally we need to combine all of these datasources into one SingleCompassGps for searchable. Add this last bean:


import org.compass.gps.impl.SingleCompassGps

compassGps(SingleCompassGps) {
compass = ref('compass')
gpsDevices = [compassGpsDevice, anotherUniqueCompassGpsDevice /* ~add the other sources in here */ ]
}

Thanks to the original thread for guidance. Hopefully this compilation of guidance will help someone else save some time. The major additions beyond the original thread are to use the sessionfactorylookup. You can see these objects in SearchableGrailsPlugin.groovy - we are simply re defining them with new datasources!





Friday, February 17, 2012

Undocumented? Grails scaffolding feature. Widgets

Present in old docs, but weirdly not the latest, you can request a different widget on scaffolded pages for your domain class.

In the domain's scaffold closure:


def constraints = {
        myStringField(widget:'textarea')
}

Make a file list quickly

Need to write a bunch of filenames to a list (say to build a resources list for a Grails plugin or html page) because you can't include just folders? But you don't want to type them all out individually?

Open that good old standby, cmd.exe  or terminal and navigate to the root folder containing the files.

[Windows] command: DIR *.* /s /b > ../yourlistname.LST

[Linux/OSX] command:  find * . > yourlistname


You can easily add extensions to the DIR and find commands to find only a particular kind of file (DIR *.js for example) Open in your favorite text editor (why not try notepad++ or textmate) use some regex-replacing..


Lets try removing directories from the file list (this could also be done with additional filters in the find commands above):



  • In notepad++, turn on regular expression searching, and check 'mark line'
  • Search for [\w+][\.][\w+], which will bring up every line with a . in it (a valid extension)
  • Invert the bookmarking with Search->Bookmark->Inverse Bookmark
  • Finally delete the now bookmarked lines without file extensions by Search->Bookmark->Delete Bookmarked Lines



And your done.

Thursday, February 16, 2012

Fun fact: Grails GString's require double quotes

Will work:


def element = "<script type=\"text/javascript\" src=\"${g.resource(dir: 'folder', file: 'file.js')}\"></script>"
out <<  element.toString()


Wont work:


def element = '<script type="text/javascript" src="${g.resource(dir: "folder", file: "file.js")}"></script>'
out <<  element.toString()


And by "wont work" the groovy expressions in the GString will not be evaluated, they will be delivered literally. Note the double quotes beginning and ending the GString in the first example.

I knew this. Yet I was dissecting why my unit test was failing for over an hour. Turns out junit can't detect silly human error! Since I re-learnt this lesson, I thought I would share it with you, blogosphere!