Please note that this question is not directly related to the display of url in Grails. It's about how URL mappings are defined.
When viewing UrlMappings.groovy we see something like the following:
class UrlMappings { static mappings = { "/foo" (controller: "foo", action: "myaction") "/bar" (controller: "bar", action: "myaction") "404" (controller: 'error') } }
The brackets indicate that there is a function / method call. In my opinion, the line
"/foo" (controller: "foo", action: "myaction")
executes a function called /foo UrlMappings . If UrlMappings does not contain a function called /foo , it will consider the closing delegate.
My problem is that the lines you can use inside mappings = { .. } are not limited. You can add any definition you want. For instance:
"!%&/()" (controller: "foo")
So there must be some kind of dynamic way to define these functions. I could not come up with a solution defining these functions inside the UrlMappings class. So I tried to come up with a solution using the closure delegate.
Using a simple groovy script, I tried the following, which worked fine for me:
def mappings = { "/foo" (controller: "foo", action: "myaction") "/bar" (controller: "bar", action: "myaction") "404" (controller: 'error') "!%&/()" (controller: "foo") } class MyMap extends LinkedHashMap { @Override public Object get(Object key) { if (!this.containsKey(key)) { this.put(key, { Map map -> println "$key called: $map" }) } return super.get(key); } } mappings.delegate = new MyMap() mappings()
Therefore, when /foo needs to be executed inside closing mappings , groovy will look for a key named /foo in my delegate map. Therefore, the get() MyMap . If a key with this name does not exist, a new one will be created. The key value is always a closure that takes the card as a parameter.
When I execute the script, I get:
/foo called: [controller:foo, action:myaction] /bar called: [controller:bar, action:myaction] 404 called: [controller:error] !%&/() called: [controller:foo]
And so it worked. However, I do not know if this uses the graph. Maybe there is a simpler solution?
So my first question is: Is there another way (maybe easier)? / How does Grails resolve these function calls?
Another question came up when I experimented with Maps as delegates.
Look at this:
def closure = { "foo" (arg: "foo") } closure.delegate = ["foo": { Map map -> println "called: $map" }] closure()
I expected this piece of code to print the line called: [arg:foo] . However, I get:
groovy.lang.MissingMethodException: No method signature: Test.foo () is applicable for argument types: (java.util.LinkedHashMap) values: [[arg: foo]]
Then I tried to execute the following results (as expected) with the same exception:
def delegate = new LinkedHashMap() delegate["foo"] = { Map map -> println "called: $map" } closure.delegate = delegate closure()
However, if I do this:
class MyLinkedHashMap extends LinkedHashMap { } def delegate = new MyLinkedHashMap() delegate["foo"] = { Map map -> println "called: $map" } closure.delegate = delegate closure()
He works.
So my second question is: Why does this not work with a simple LinkedHashMap , but it works with MyLinkedHashMap (which does not change anything)?