Skip to content

Instantly share code, notes, and snippets.

@milankinen
Last active December 15, 2015 10:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save milankinen/5249593 to your computer and use it in GitHub Desktop.
Save milankinen/5249593 to your computer and use it in GitHub Desktop.
package com.tunkkaus
import org.grails.plugin.resource.ResourceMeta
import org.grails.plugin.resource.ResourceProcessor
import org.grails.plugin.resource.mapper.MapperPhase;
import org.grails.plugin.resource.module.ModuleBuilder
import org.springframework.util.AntPathMatcher
/**
* We are using prefix '_' because we want that this resource mapper
* is FIRST mapper of the processing chain. Sorting is done by name
* so '_' prefix gives us advantage compared to other mappers.
*
*/
class _MultiModuleResourceMapper {
private static final String MULTIPLE_RESOURCES_PREFIX = "/__multiModule__"
private static long declaredResourcesNum = 0
static defaultIncludes = [ '**/*.coffee', '**/*.js', '**/*.handlebars' ]
static String getResourcePrefix() { "${MULTIPLE_RESOURCES_PREFIX}.${declaredResourcesNum++}/" }
static boolean isMultipleResourceFile(String uri) { uri?.startsWith(MULTIPLE_RESOURCES_PREFIX) }
static String getOriginalURI(String uri) { uri?.replaceFirst(MULTIPLE_RESOURCES_PREFIX + '\\.\\d+/', '/') }
private static boolean canBeRenamed(String filepath) {
if(filepath.startsWith(MULTIPLE_RESOURCES_PREFIX)) return false
// behaviour can be modified by chancing defaultIncludes
def matcher = new AntPathMatcher()
return defaultIncludes.find { matcher.match(it, filepath) } != null
}
/**
* Call this from resources.groovy! This modifies resources plugin methods
* so that they support multiple resource definitions inside multiple modules.
*/
static void enable() {
def origResource = ModuleBuilder.metaClass.getMetaMethod("resource", [Object] as Object[])
def origGetURL = ResourceProcessor.metaClass.getMetaMethod("getOriginalResourceURLForURI", [Object] as Object[])
assert origResource != null && origGetURL != null
// override modulebuilder's resource function to apply a unique prefix
// because file will have an unique prefix, then it is interpreted as
// "new" file for resources plugin
ModuleBuilder.metaClass.resource = { attrs ->
if (attrs instanceof String) {
if (canBeRenamed(attrs)) {
attrs = resourcePrefix + attrs
}
} else if (attrs instanceof Map && attrs.url instanceof String) {
if (canBeRenamed(attrs.url)) {
attrs.url = resourcePrefix + attrs.url
}
}
origResource.invoke(delegate, attrs)
}
// this method is called before resource processing chain begins
// this gets the file handle to the original resource. Since we have
// added a prefix, the actual file is never found unless we tweak this
// method a little bit to transform the modified filename back to the
// original one (just for this method)
ResourceProcessor.metaClass.getOriginalResourceURLForURI { uri ->
if (isMultipleResourceFile(uri)) {
uri = getOriginalURI(uri)
}
return origGetURL.invoke(delegate, uri)
}
}
// using first possible mapper phase
def phase = MapperPhase.GENERATION
def map(resource, config) {
def url = resource?.actualUrl
// now we can process the resource whatever we want. Let's just change
// resource's URLs back to the original file's, so that later processors
// can deal with right file.
if (isMultipleResourceFile(url)) {
url = getOriginalURI(url)
resource.actualUrl = resource.originalUrl = resource.sourceUrl = url
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment