Tuesday 26 March 2013

Tuning Grails resources plugin to support multiple modules for same file

A long time since my previous blog post. Deepest apologizes, I have had so little time for my "hobbies"...

But now! I faced so frustrating issue in my work so that I must forward it to you. We have been developing our system quite a time already. Real problems started when it was time for the first test deployment to the actual server. We are using Grails as our controller layer which includes a wonderful resource plugin that enables us to use newest techiques in our assets easily (such as Coffeescript or LESS). The production mode will be caching, zipping and minimizing all resource modules in order to reduce clients' overhead. These are not used in development mode. So, during the deployment I discovered that the resource plugin has a "bug" which prevents the definition of same file to multiple modules.

After a Google moment, I found that the plugin's author wasn't planning to support such a feature (GPRESOURCES-166). We had two options: either divide our code into multiple mini-mini modules (which reduces the usefulness of modules a lot) OR try to go around the bug/feature.

So, after a long moment of discovering plugin's source codes, I managed to find a detour. It is not an elegant way but working well enough. The resouces plugin uses a chain of resource mappers to transform original files into processed files. The bundling and bundle renamining (for cache prevention) are dealt by using file paths as identifiers for files. So so... The best bet is to rename the file with a unique prefix before the resource is "registered" into module. When registering is done, the resources plugin tries to fetch the resource file based on its defined filename. Thus, we need to translate the modified filename back to the original one so that resources plugin can find the actual file. When the file has been fetched, resources plugin starts to process the file with its processing chain. We must add our own custom resource mapper to the beginning of that chain to translate processed resource's URL back to the original so that later processors can deal with right files.

The solution is to override two resource plugin's methods by using meta-programming. First we modify the filename during the resource definition. Then we rename it back to the original for file handle fetching. After that, we use our own resource mapper to change resource's URLs back to the normal at the beginning of the processing chain. And hóle! Now the plugin supports multiple module definitions for same file also with cached-resources plugin.

Here is the actual code:
You must place that into your grails-app/resourceMappers folder. After that you must enable support by calling MultipleStaticResources.enable() from your resources.groovy bean definition closure.
Of course this solution may conflict with our own resource mappers, but for me it works well enough: resource mappers for CoffeeScript, Handlebars, LESS, zipped and bundled/cached resoures (and also few own) are working fine with this hack.

Happy hacking!

No comments:

Post a Comment