Intellij IDEA + Groovy + better static validation? - grails

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)

Related

Groovy #Delegate class annotations are not supported in the pipeline shared library jenkins

jenkins ver. 2.89.4
workflow-cps ver 2.42
When i try to run a pipeline that loads the library, i get the following exception:
Unsupported expression for CPS transformation # line -1, column -1
The Groovy class to which the link is referenced:
final class Bindings implements Serializable{
#Delegate
private final Map map
Bindings(Map map) {
this.map = map
}
#Override
boolean containsKey(Object key) {
return true
}
#Override
Object get(Object key) {
return map.getOrDefault(key, null)
}
}
You can reproduce an exception in the pipeline or in the library like this:
def map = ["key":"value"]
Bindings bindings = new Bindings (map)
It's a known Jenkins issue: JENKINS-45901
This has been open since August 2017. Looks like it isn't going to be fixed anytime soon:
Not sure if there is any particular place where Groovy language support (or lack thereof) is exhaustively documented, but at any rate I would not expect this issue or anything like it to be addressed. The focus going forward is allowing external process execution, not wasting any more time on the CPS engine except in cases of security vulnerabilities or serious regressions.
I tried the combination of #Delegate with #NonCPS but the latter doesn't seem to have any effect on fields.
You'll have to implement the delegate methods yourself or try another solution.
Not sure offhand what the issue here is (other annotations do work). Maybe only applies to source-generating annotations. Anyway for any non-trivial library it will most likely make use of Groovy language features Sorry but exotic features are not generally available in Pipeline script. Lack of support for Groovy class annotations is not explicitly documented (as far as I can see) either in this plugin or in the workflow-cps-plugin https://github.com/jenkinsci/workflow-cps-plugin.

Groovy syntax, named block?

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.

Grails override a plugin service static method

I'm using a plugin which has this Service:
package grails.plugins.imports
class ImportsService {
static rabbitQueue = "${grails.util.Holders.grailsApplication.metadata['app.name']}ImportRows"
....
}
While this works fine when using run-app; i.e grails run-app, this is wreaking havoc when attempting to run as a war; grails run-war.
2014-09-09 15:54:25,069 [localhost-startStop-1] ERROR StackTrace - Full Stack Trace:
java.lang.NullPointerException: Cannot get property 'metadata' on null object
at org.codehaus.groovy.runtime.NullObject.getProperty(NullObject.java:56)
at org.codehaus.groovy.runtime.InvokerHelper.getProperty(InvokerHelper.java:169)
at org.codehaus.groovy.runtime.callsite.NullCallSite.getProperty(NullCallSite.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java:227)
at grails.plugins.imports.ImportsService.<clinit>(ImportsService.groovy:9)
Short of forking the plugin, any suggestions here?
Grails 2.3.10
Thanks in advance,
Todd
So, I know your goal was to avoid forking the plugin, but I think the issue is that the plugin was written in earlier days when you would get the grailsApplication object through a holder class. That's not really recommended these days (see Burt Beckwith's post on the subject), but there are options.
The easiest way for your plugin to get the grailsApplication object would be through dependency injection:
class ImportsService {
def grailsApplication
static rabbitQueue = "${grailsApplication.metadata['app.name']}ImportRows"
//....
}
Though in this case, since all you need is the app.name, it might be better to:
class ImportsService {
static rabbitQueue = "${grails.util.Metadata.current.'app.name'}ImportRows"
//....
}
You might try manipulating the plugin code in a local copy to see if that fixes the issue. GGTS makes that fairly easy by providing the plugins in the project explorer view. If that change works, and you can submit a patch to the plugin developer, you might be able to have the fix become part of the official release.

Mixing private and public/protected methods of the same name causes multimethods to be disabled and is forbidden to avoid surprising behaviour

I just upgraded one of my Grails apps to 2.2.0 which is using Groovy 2.0 and I am now getting this compile error:
Mixing private and public/protected methods of the same name causes
multimethods to be disabled and is forbidden to avoid surprising
behaviour. Renaming the private methods will solve the problem.
Based on the following code:
def getRootDomain(key) { }
private getRootDomain() { }
It's an easy fix but I'd really like to understand the why better. Can someone explain this to me?
It's because of the way groovy chooses which method to call, and its potential incompatibility with java.
Have you seen this thread on the mailing list?
http://groovy.329449.n5.nabble.com/mixing-public-private-overloaded-methods-causes-compilation-error-td367147.html

Groovy - How to replace methods of existing java classes

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.

Resources