I'm starting to learn grails and there is some groovy syntax that I don't get at all, and is undocumented as far as I can tell because I don't know what it's called.
What are 'grails', 'views', and 'gsp' in the code below?
grails {
views {
gsp {
encoding = 'UTF-8'
// ...
}
}
}
Thanks!
p.s. I feel like an idiot for not being able to figure this out...
This is an example of DSL (domain specific language) code. In Groovy, many DSLs are implemented as Groovy code, although sometimes it can look pretty funky. This code block is run as Groovy code, and it's more clear that it's code if you add in the omitted optional parentheses:
grails({
views({
gsp({
encoding = 'UTF-8'
// ...
})
})
})
and more so if we replace the property set call to the equivalent method call
grails({
views({
gsp({
setEncoding('UTF-8')
// ...
})
})
})
If you ran this in a console or as part of other Groovy code or in a Grails app it would fail, because there's no 'grails' method taking a closure as an argument, or a similar 'views' or 'gsp' method, and there's no setEncoding(String) method either. But when run as DSL code, often the code is run inside a closure whose delegate is set to a DSL helper class that handles the resulting methodMissing and propertyMissing calls.
This helper looks at the method name and the number and/or types of the method arguments (or property name and value type) and if they are valid for the DSL, it does the corresponding work, otherwise it throws a MissingMethodException/MissingPropertyException, or a DSL-specific exception, or handles the problem some other way.
In this case, there's a configuration DSL handler that translates these method calls into config property calls.
There are several other DSLs in use in a Grails app that work the same way. The mapping and constraints blocks in domain classes, the grails.project.dependency.resolution block in BuildConfig.groovy, etc. All are evaluated as Groovy code, and the missing method/property calls configure GORM mappings, constraint definitions, plugin dependencies, etc.
Programming Groovy 2 is a particularly good Groovy book for learning DSLs.
Related
I have a method in a Grails service:
void methodA(Car car) { }
and a second method in the same class calling this method:
void methodB() {
methodA("A string")
}
But intellij doesnt even warn for this clearly incorrect usage.
I understand that a methodA can be injected with a String signature, but is there a way to make Intellij act or expect less dynamic behavior?
It would be nice to code statically with Groovy yet continue to have some kind of flag.
It would be a real downer if one has to choose so drastically between Java and Groovy.
I am using Grails 2.3.7, and I believe it is using Groovy 2.x.y
I am using IntelliJ IDEAD 12.1.6 and it does provide a warning for your example:
On the Idea Groovy Plugin page it states the following:
Inapplicable method calls marked as warnings
Do you have that plugin installed/enabled? (check under file->settings->plugins)
I am using the AWS plugin for grails. I am trying to use the sesMail closure from a Groovy class, not a service or controller e.g. in a method:
String msgId = sesMail {
from props.from
replyTo props.replyTo
to prop.to
html props.body
}
I am getting an error saying sesMail is not a method of the Groovy class. My questions are:
How can I call a plugin closure that is ordinarily available in grails service and controllers from a regular Groovy class
Following that I'm curious how these closures are defined and made available in the service and controller. I can't find a definition of sesMail in the plugin.
thanks
Good Question. And I appreciate that you are curious to find the root cause of your problem. Here are your answers:-
You can directly call the SendSesMail bean to send mail instead of calling the sesMail closure. Your groovy class would look like:
class MyGroovyClass{
def sendSesMail
def sendSomeMails(){
String msgId = sendSesMail.send{
from props.from
replyTo props.replyTo
to prop.to
html props.body
}
}
}
As you can see SendSesMail is not a service class it is just a POGO, so it will not be autowired in this groovy class unless you define that in resources.groovy. So:
Something like:
//resources.groovy
beans = {
sendSesMail(grails.plugin.aws.ses.SendSesMail)
}
Also keep a note by using the above method directly you would be bypassing grails.plugin.aws.ses.enabled configuration. So you have to handle it explicitly.
You can very well find the definition of sesMail method which is metaClasses over target classes to take a Closure as an argument here in MetaClassInjector. This class is basically used to inject/add dynamicMethods from the plugin definition. You can find it as
AwsGrailsPlugin (Line: 37) --> AwsPluginSupport (Line: 86) --> MetaClassInjector (Line: 50)
You can also see in MetaClassInjector (around line 45 and 46), the target classes are controller and service classes. Hence you find the closure sesMail available in those 2 artefacts.
I hope it is clear enough to address your curiousness. :)
How do I replace methods of an existing java class (GrailsDataBinder in my case).
I read that method calls for java classes doesnt go through invokeMethod, and hence it doesn't work, but I think there would be some solution.
I just tried this
GrailsDataBinder.metaClass.static.createBinder = {Object target, String objectName ->
throw new RuntimeException()
}
And this
GrailsDataBinder.class.metaClass.static.createBinder = {Object target, String
objectName -> throw new RuntimeException()
}
But that did not seem to have replaced the method, as my closure isn't being invoked, but instead the original method executes.
update
I just found that the closure is being executed if I call the createBinder from the test class itself - so it works and method is replaced
void testDataBinder() {
GrailsDataBinder.createBinder(null, null)
}
However When it is invoked from DataBindingUtils, it always executes original method (DataBindingUtils is also a java class)
Following is the code inside DataBindingUtils that invokes the method.
binder = GrailsDataBinder.createBinder(object, object.getClass().getName());
Note : There are some similar questions asked earlier, but none of them have worked for me.
This is disappointing once you see the power of Groovy. However, as you already know, many of the cool metaclass etc. features available in Groovy simply don't work as you would like on Java classes.
If you are trying to override, stub or mock stuff on a Java class for unit testing etc., I advise looking into Spock, because it uses 'magic' that actually works on Java classes also.
If you are try to override methods for some other reason, you should try extending the Java class with a Groovy class or 'wrapping' the Java class with a Groovy class to gain the metaclass features you want when external classes call you classes methods. Unfortunately this still won't allow you to intercept calls that the Java class makes to itself.
I am attempting to add some string utility methods to the String class by utilizing Groovy's metaclass functionality. Right now I have something like this in my init closure in my BootStrap.groovy script:
String.metaClass.upper = {
delegate.toUpperCase()
}
The problem is that this upper method is only available within the scope of the BootStrap...trying to use it anywhere else in the Grails app does not work and I get method missing errors.
Is there any way to make those methods available everywhere?
Does this happen in both testing and running contexts? There is a bug where all metaClass changes get rolled back after each unit test: http://jira.grails.org/browse/GRAILS-8596
I was trying to define a dynamic method using the GroovyDSL scripting capabilities in IntelliJ. The starting guide "Scripting IDE for DSL awareness" gives you a good idea on how to get started with this. The dynamic method definition in my DSL is more complex than the example though and I am not quite sure how to build this. You can imagine it working like dynamic finder methods in Grails except that you can combine an arbitrary number of criteria with a boolean And keyword in any order. All keywords are not defined in the class that I am invoking the method on but could be defined in the GDSL file. The method always starts with submitTransactionWith. Here's an example:
clientDSL.submitTransactionWithCriteria1AndCriteria2AndCriteria3(arg1, arg2, arg3)
I started out with this but that only works for one criteria and doesn't take into account that you can combined multiple criteria in any order.
def ctx = context(ctype: "my.client.ClientDSL")
contributor(ctx) {
['Criteria1', 'Criteria2', 'Criteria3'].each {
method name: "submitTransactionWith${it}",
type: 'java.util.Map',
params: [args: 'java.util.Collection']
}
}
I was wondering if there's special support for that kind of dynamic method. I'd also be interested in how the DSL for Grails is modeled internally in IntelliJ.
The Grails DSL in located in ${idea.home}/plugins/GrailsGriffon/lib/standardDsls
It would probably help you for your problem. They create string arrays of all the method name combinations ahead of time, and then just iterate through them in their contributor creating a method using the arrays of strings for names.