What does this syntax mean in Groovy?
class CreateMessagePage extends Page {
static at = { assert title == 'Messages : Create'; true }
static url = 'messages/form'
static content = {
submit { $('input[type=submit]') }
MyVeryStrangeForm { $('form') }
errors(required:false) { $('label.error, .alert-error')?.text() }
}
}
(taken from Spring MVC Test HtmlUnit manual)
The question is about Groovy and I would like know the answer in Groovy terms.
What is content
? Is it a static variable? Is its name is random or predefined by base class of Page
?
What is =
(equal sign) after it? Is it an assignment operator?
What is at the right hand side of =
? Is this a closure? Or if this an anonymous class? Or if these are the same?
What is submit
inside curly braces?
Is this a variable? Why there is no assignment operator after it then?
Is this a function definition? Can I define functions in arbitrary places in Groovy? If this is a function definition, then what is errors
then?
Is submit
is a function call, receiving { $('input[type=submit]') }
as a parameter? If yes, then where is this function can be defined? For example, where is MyVeryStrangeForm
defined (is nowhere)?
If this was function call, then it won't work since it's undefined...
Quick answer to all questions: it's a block of code, like anonymous function, called closure in Groovy.
See http://www.groovy-lang.org/closures.html
In Groovy you can reference/pass/set such closure, as in any Functional Language.
So this:
static at = { assert title == 'Messages : Create'; true }
means that class field at
will be set to this closure (notice, not result of closure execution, but closure itself, as block of code). Type of at is omitted there, but it could be static def at
or static Object at
, or static Closure at
This code could be executed anytime later, in different context, with title
defined, etc.
This:
submit { $('input[type=submit]') }
means calling a function submit
with closure as argument.
If you want to write own function like this, it should be something like:
def submit(Closure code) {
code.call()
}
Brackets could be omitted, so it could be written as submit({$('input[type=submit]')})
. Same for other function as well, it could be println 'hello world!'
instead of println('hello world'
).
There's also a common practice to define closure as last argument, like:
def errors(Map opts, Closure code) {
....
}
at this case you could pass first arguments as usual, wrapped in brackets, and closure outside:
errors(required:false) { ...... }
same to:
errors([required: false], { ..... })