Groovy trait: cannot find matching method error - groovy

I'm trying to use Spock and Groovy for testing. I have written simple class/trait for testing as follows:
import com.plomber.user.domain.UserDto
import groovy.transform.CompileStatic
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
#CompileStatic
trait SampleUsers {
UserDto John = createDto(1, "john#example.com", "simpleJohn1")
UserDto Bob = createDto(2, "Bob#example.com", "simpleBob1")
static private UserDto createDto(Integer id, String email, String password) {
return UserDto.builder()
.id(id)
.email(email)
.password(new BCryptPasswordEncoder().encode(password))
.build()
}
}
I'm getting following error when I try to compile it:
Error:(10, 20) Groovyc: [Static type checking] - Cannot find matching method com.plomber.SampleUsers#createDto(int, java.lang.String, java.lang.String). Please check if the declared type is right and if the method exists.
If I assign John and Bob fields using UserDto.builder().id(id) ... instead of createDto() method - it works as expected. Do I miss something?

From groovy docs:
Traits with static methods cannot be compiled statically or type
checked. All static methods/properties/field are accessed dynamically
(it’s a limitation from the JVM).
http://docs.groovy-lang.org/next/html/documentation/core-traits.html#_static_methods_properties_and_fields

Related

Add strong typing to objects from JsonSlurper

I'm having some trouble getting typing to work with the JsonSlurper in Groovy. I'm fairly new to Groovy, and even newer to adding strong types to it - bear with me.
Right now I've created a trait which defines the general shape of my JSON object, and I'm trying to cast the results of parseText to it.
import groovy.json.JsonSlurper
trait Person {
String firstname
String lastname
}
def person = (Person)(new JsonSlurper().parseText('{"firstname": "Lando", "lastname": "Calrissian"}'))
println person.lastname
This throws
Exception in thread "main" org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '{firstname=Lando, lastname=Calrissian}' with class 'org.apache.groovy.json.internal.LazyMap' to class 'Person' due to: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: Person(org.apache.groovy.json.internal.LazyMap)
...
I can see why my code doesn't make sense, I'm not trying to change the type of the data (casting), I'm just trying to let my IDE know that this is what's inside of my object.
Is it possible to at least add code completion to my JSON objects? I'd love to get runtime type checking, as well, but it's not necessary.
you could try to use delegate
this allows to wrap class around map
import groovy.json.JsonSlurper
class Person {
#Delegate Map delegate
String getFirstname(){ delegate.get('firstname') }
String getLastname(){ delegate.get('lastname') }
}
def person = new Person(delegate:new JsonSlurper().parseText('{"firstname": "Lando", "lastname": "Calrissian"}'))
println person.lastname
or for example use Gson for parsing:
#Grab(group='com.google.code.gson', module='gson', version='2.8.5')
import com.google.gson.Gson
class Person {
String firstname
String lastname
}
def person = new Gson().fromJson('{"firstname": "Lando", "lastname": "Calrissian"}', Person.class)
assert person instanceof Person
println person.lastname
This actually is a cast and Groovy will try to turn your Map into said object.
From the docs:
The coercion operator (as) is a variant of casting. Coercion converts object from one type to another without them being compatible for assignment.
The way this works for a POJO is to construct a new object using the Map-c'tor. This will either unroll into calling setters or works directly with static compilation.
Be aware, that using maps with excess keys will lead to errors. So I'd only use this for toy projects. Use a proper JSON-mapper like e.g. Jackson instead.
So the solution here is to not use a trait (which is basically a interface) but a regular class.

How to get a Geb module instance with its declared class?

Up to Geb version 0.10 the example code below has worked just fine:
package whatever
import geb.Module
import geb.Page
import geb.spock.GebSpec
class ExampleSpec extends GebSpec {
def 'MODULE - Y U NO HAVE THE RIGHT CLASS?'() {
when:
ExamplePage page = to ExamplePage
then:
verifySomething(page.theModule)
}
boolean verifySomething(ExampleModule module) {
// ...
}
}
class ExamplePage extends Page {
static content = {
theModule { module ExampleModule }
}
}
class ExampleModule extends Module {
}
I wanted upgrade to the latest 0.13.1 but apparently the breaking (regressive I would say) change has been introduced which results with:
groovy.lang.MissingMethodException: No signature of method:
geb.navigator.NonEmptyNavigator.verifySomething() is applicable for
argument types: (geb.content.TemplateDerivedPageContent) values:
[whatever.ExamplePage -> theModule: whatever.ExampleModule]
I've noticed that the same happens but with different class since version 0.11, the exception message is as follows:
groovy.lang.MissingMethodException: No signature of method:
geb.navigator.NonEmptyNavigator.verifySomething() is applicable for
argument types: (geb.content.SimplePageContent) values: [theModule -
SimplePageContent (owner: whatever.ExamplePage, args: [], value:
null)]
Why module declared with a given, specific class looses it at runtime? How to prevent that?
Objects implementing Navigator interface (which includes classes extending from Module) and returned from content definitions are wrapped with TemplateDerivedPageContent objects which delegate to the underlying object but also allow to produce a meaningful path to the object for error reporting.
The wrapping of modules worked in older versions of Geb, then it got inadvertently removed and now it's back. Even though you can still call all the methods of the module when it's wrapped thanks to TemplateDerivedPageContent dynamically delegating to the underlying object you run into trouble in cases like yours - when you want to strongly type your code that uses modules. Therefore I'm still undecided what we should sacrifice here - better error reporting or ability to strongly type and this wrapping might be removed in a future version of Geb.
Luckily there is a workaround - if you want to strongly type code that consumes modules then use a getter instead of a content definition to declare them. In your case it would be:
class ExamplePage extends Page {
ExampleModule getTheModule() {
module ExampleModule
}
}

Optional arguments on interface and class can conflict

I have just come across an interesting gotcha where optional arguments on an interface and the implementing class can conflict.
I found this out the hard way (school boy error) whilst experimenting. You cannot spot it in the debugger and I assumed it was me messing up the dependency injection.
I'm guessing this is so an alternative interface can give a differing view on what default behaviour should be?
Is there a compiler warning or style cop rule to help point this out?
public interface MyInterface
{
MyStuff Get(bool eagerLoad = true); //this overrules the implementation.
}
public class MyClass : MyInterface
{
public MyStuff Get(bool eagerLoad = false) //will still be true
{
//stuff
}
}
Remember default arguments are a compile-time feature. The compiler picks up the default argument based on the static type of the reference in question and inserts the appropriate default argument. I.e. if you reference is of the interface type you get one behavior but if the reference is of the class type you get the other in your case.

Ninject: Binding an interface with a generic that is also an interface

I have searched this issue but with no luck. Here we go.
Suppose I have an interface:
interface IQueryRepository<T> where T : class
and I want to bind any requests for:
IQueryRepository<IClient>
to:
ConcreteQueryRepository<Client>
I've tried the obvious:
Bind<IGenericQueryRepository<IClient>>().To<ConcreteQueryRepository<Client>>()
But I get an error:
ConcreteQueryRepository<Client> cannot be used as type parameter 'TImplementation' in
the generic type or method 'Ninject.Syntax.IBindingToSyntax<T>.To<TImplementation>()'
There is no implicit reference conversion from 'ConcreteQueryRepository<Client>'
to 'IGenericQueryRepository<IClient>'
But I don't understand why since GenericQueryRepository implements IGenericQueryRepository and Client implements IClient.
I would like Ninject to give me a concrete generic repository where T is Client. I want this to avoid using concrete types in the code.
Can it be done?
This has to do with Covariance and Contravariance.
In your question you mentioned the following:
... GenericQueryRepository implements IGenericQueryRepository and Client implements IClient.
Let's make it simpler by using fruits: Fruit implements IFruit. We'll also create a Tree class.
public interface IFruit { }
public class Fruit : IFruit { }
public class Tree<T> where T : IFruit { }
Tree<IFruit> tree = new Tree<Fruit>() // error
This will reproduce the same kind of error you're experiencing. Why? Simple.
Though Fruit implements IFruit, an Fruit Tree doesn't implement a IFruit Tree. There is no cast possible between the Fruit Tree and the IFruit Tree, although you would expect it. They are both Trees, but with a different type parameter. The fact that their type parameters are related to each other, doesn't matter.
In other words: there is no cast possible between the Fruit Tree and the IFruit Tree, because their type parameters don't match.
In general, when casting with generics, make sure their type parameters match. However, there are a few exceptional cases. See Variance in Generic Interfaces.
In your case, you could fix it by using IClient as type parameter for the GenericQueryRepository class. Doing this will allow casting because the type parameters match. But I don't know your application architecture, so this fix might be inapplicable in your case.
EDIT: To make it easier to understand, copy paste the code below and see what the compiler says.
interface IFruit { }
class Fruit : IFruit { }
interface ITree<T> where T : IFruit { }
class Tree<T> : ITree<T> where T : IFruit { }
class Program
{
static void Main(string[] args)
{
ITree<Fruit> test1 = new Tree<Fruit>(); // compiles: type parameters match
ITree<IFruit> test2 = new Tree<Fruit>(); // fails: type parameters don't match
ITree<Fruit> test3 = new Tree<IFruit>(); // fails: type parameters don't match
ITree<IFruit> test4 = new Tree<IFruit>(); // compiles: type parameters match
IEnumerable<IFruit> test5 = new List<Fruit>(); // compiles: this is one of the exceptional cases
}
}
That should clear things up about what is and what is not possible.
I've had the same problem when trying to bind a Dapper query to an interface type, thinking about it, it seems to make sense that Dapper can't instantiate an Interface type.
The interface is only a contract and does not know about how to instantiate a concrete implementation of it.
Dapper needs a type that is concrete implementation of the interface type otherwise Dapper would also have to know which concrete implementation of the interface to instantiate, and in that case Dapper would behave like a DI container which, indeed, it isn't.

Implicit Conversion Not Working for Dynamic Type

I am running into a problem when trying to implicitly convert one of my dynamic types. There are two assemblies with definitions similar to the following:
Configuration.dll:
public class ConfigurationValue : DynamicObject
{
public ConfigurationValue(string val)
{
//...
}
//...
public static implicit operator string(ConfigurationValue val)
{
return val.ToString();
}
}
There is another class in this dll called Configuration with a member variable called Instance (to make the class singleton). This variable holds the ConfigurationValue instances in a dictionary and is of type dynamic. This allows me to do this following:
Server.dll:
//...
if (Configuration.Instance.SecurityLevel != "Insecure")
{
//...
}
Assuming that SecurityLevel is in the dictionary.
This if statement appears verbatim in my code and always fails with the following error:
{"Operator '!=' cannot be applied to operands of type 'System.Dynamic.DynamicObject' and 'string'"}
Previously, when these two classes were in the same assembly, this code worked fine. Can anyone tell me what I'm doing wrong here?
Thanks,
Max
Solved the problem, a little embarrassing actually, I forgot to change the container class for ConfigurationValue (e.g. the type of Configuration.Instance) from internal to public when I moved it to the new assembly, so of course the type couldn't be resolved and the implicit conversion was not found
Try
var SecurityLevel = new ConfigurationValue("Insecure");

Resources