In Gradle, how do you perform validation of lazily evaluated properties (on extensions)? - gradle

Is there a way to validate a property value when the property is evaluated? I can't do it in the getter because that returns the Property object - I want the validation to run only when the actual value is calculated (i.e. I want to be lazy evaluation friendly).
They show extensions using the Property object here:
https://docs.gradle.org/current/userguide/lazy_configuration.html#connecting_properties_together
However, they don't explain how to do property validation when the value is calculated. Here is the snipet of code from the Gradle documentation provided example:
// A project extension
class MessageExtension {
// A configurable greeting
final Property<String> greeting
#javax.inject.Inject
MessageExtension(ObjectFactory objects) {
greeting = objects.property(String)
}
}
If I wanted to make sure the value of greeting was not equal to test, then how would I do that when it is evaluated?

For most use cases, it should be sufficient to just validate the property value once you resolve it in your task or in other internal parts of your plugin. Only a few extensions are actually designed to be consumed by other plugins or the build script.
Gradle does not provide some validation that can be attached to a property, however you can build this functionality on your own like in the example below:
class MessageExtension {
private final Property<String> _greeting
final Provider<String> greeting
#javax.inject.Inject
MessageExtension(ObjectFactory objects) {
_greeting = objects.property(String)
greeting = _greeting.map { value ->
if (value.equals('test'))
throw new RuntimeException('Invalid greeting')
return value
}
}
def setGreeting(String value) {
_greeting.set(value)
}
def setGreeting(Provider<String> value) {
_greeting.set(value)
}
}
project.extensions.create('message', MessageExtension)
message {
greeting = 'test'
}
println message.greeting.get()
I turned the Property into a backing field for a Provider that runs the validation when resolved. If you do not want to throw an exception, but just return an empty Provider, you may replace the map with a flatMap.

Related

MapStruct Spring Page to custom object conversion includes check

I am using MapStruct to convert a Page object to a custom object of my application. I am using this mapping in order to convert the content field of the Page object to a list of custom objects found in my data model:
#Mapping(target = "journeys", source = "content")
While this works OK and does convert the elements when content is present, this does not work correctly in case of no Page content. Taking a look at the code seems to show that the following check is added in the generated mapper class:
if ( page.hasContent() ) {
List<JourneyDateViewResponseDto> list = page.getContent();
journeyDateViewPageResponseDto.setJourneys( new ArrayList<JourneyDateViewResponseDto>( list ) );
}
When this is added the mapping action of the inner objects is omitted, meaning that I end up with a null list. I am not really sure as to why and how this check is added but I would like to find a way of disabling it and simply end up with an empty list of elements. Is there a way this can be done using MapStruct?
MapStruct has the concept of presence checkers (methods that have the pattern hasXXX). This is used to decide if a source property needs to be mapped.
In case you want to have a default value in your object I would suggest making sure that your object is instantiated with an empty collection or provide an #ObjectFactory for your object in which you are going to set the empty collection.
e.g.
Default value in class
public class JourneyDateViewPageResponseDto {
protected List<JourneyDateViewResponseDto> journeys = new ArrayList<>();
//...
}
Using #ObjectFactory
#Mapper
public interface MyMapper {
JourneyDateViewPageResponseDto map(Page< JourneyDateViewResponseDto> page);
#ObjectFactory
default JourneyDateViewPageResponseDto createDto() {
JourneyDateViewPageResponseDto dto = new JourneyDateViewPageResponseDto();
dto.setJourneys(new ArrayList<>());
return dto;
}
}
#Mapping(target = "journeys", source = "content", defaultExpression = "java(java.util.List.of())")

Difference between "def" and "static def" in Gradle

As the title, what is exactly the difference of these two defs in Groovy?
Maybe it's a documentation problem, I can't find anything...
A method declaration without static marks a method as an instance method. Whereas a declaration with static will make this method static - can be called without creating an instance of that class - see https://www.geeksforgeeks.org/static-methods-vs-instance-methods-java/
def in groovy defines a value as duck typed. The capabilities of the value are not determined by its type, they are checked at runtime. The question if you can call a method on that value is answered at runtime - see optional typing.
static def means that the method will return a duck typed value and can be called without having instance of the class.
Example:
Suppose you have these two classes:
class StaticMethodClass {
static def test(def aValue) {
if (aValue) {
return 1
}
return "0"
}
}
class InstanceMethodClass {
def test(def aValue) {
if (aValue) {
return 1
}
return "0"
}
}
You are allowed to call StaticMethodClass.test("1"), but you have to create an instance of InstanceMethodClass before you can call test - like new InstanceMethodClass().test(true).

Gradle properties not visible inside extension container closure

I'm trying to write this custom plugin for Gradle but I'm stuck in properly passing parameters to the plugin.
inside the plugin I'm creating an extension like following:
#Override void apply(final Project p) {
p.extensions.create('myPlugin', MyPluginData.class)
then inside MyPluginData I'm handling def propertyMissing(String name, value) to receive the customer parameters I expect.
And finally inside the client application build.gradle I'm trying to configure the data:
println("From root value is " + SOME_VALUE)
myPlugin {
println("From plugin value is " + SOME_VALUE)
println("But from plugin 'findProperty' value is " + findProperty("SOME_VALUE"))
clientDataSet = {
data_1 = SOME_VALUE
data_2 = findProperty("SOME_VALUE")
data_3 = "this is a string"
SOME_VALUE is defined on my project gradle.properties, and I got the following log during build:
From root value is correct value from properties
From plugin value is null
But from plugin 'findProperty' value is correct value from properties
and then of course, while receiving data_1 SOME_VALUE is null, data_2 have the correct value and data 3 is the hard-coded string I passed.
My question:
What am I doing wrong or which configuration is missing on my plugin, so that the client application can directly reference properties from their gradle.properties files?
Edit: as requested on the comments
MyPluginData is simply extends HashMap<String, MyPluginDataSet> and MyPluginDataSet is just a few strings.
So inside propertyMissing I'm simply adding the property name to the map, and creating the MyPluginDataSet with the strings, (that later is used to generate custom tasks).
The missing property function:
def propertyMissing(String name, value) {
// Create the new data set and add to the map
def data = new MyPluginDataSet()
put(name, data)
// setup and execute the client closure to configure the data
def closure = value as Closure
closure.delegate = data
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure.run()
}
By making MyPluginData inherit from Map<>, I think you somehow "break" the property resolution process ( see ExtensionAware) and Gradle will not try to search for "SOME_VALUE" property in the different scopes (so it will not find this property from gradle properties extension)
Maybe you can try to simplify you MyPluginData class by storing an internal map instead of inheriting from Map ? something like that:
class MyPluginData {
Map<String, MyPluginDataSet> internalMap = new HashMap<>()
def propertyMissing(String name, value) {
println "Entering propertyMissing for name = $name"
// Create the new data set and add to the map
def data = new MyPluginDataSet()
internalMap.put(name, data)
// setup and execute the client closure to configure the data
def closure = value as Closure
closure.delegate = data
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure.run()
}
}

Grails: change the property name "errors"

I have a class that looks like this
#Validateable
class StudentBean{
def String name;
def String age;
def String address;
def List<ErrorBean> errors = [];
static constraints = {
age nullable : false, validator : { val, obj, errors->
if(val<10)
errors.rejectValue("age", "student.age.notQualified.message", [val] as Object[], "Student not qualified.");
}
}
}
You can see that I have a errors property in StudentBean (List). i realized that variable name has a conflict with the errors where the bean error is stored. What i did was changed the closure to something like this
age nullable : false, validator : { val, obj, errorsValue->
if(val<10)
errorsValue.rejectValue("age", "student.age.notQualified.message", [val] as Object[], "Student not qualified.");
}
but I couldnt loop the errorsValue to get the error.
When I check what errors is this is what docs says:
The errors property on domain classes is an instance of the Spring
Errors interface. The Errors interface provides methods to navigate
the validation errors and also retrieve the original values.
https://grails.github.io/grails-doc/latest/guide/validation.html
so this is the property of the domain class, probably declared but hidden.
The question is can I change the errors property name to something else?
You can not change the validation errors property.
The reason for this comes down to the philosophy behind Grails. errors exists on all Grails domain classes by convention. Much of what makes Grails powerful is not the configuration but rather the "convention over configuration". This ensures consistency between Grails projects and makes the entire framework simpler to learn and use.

Use of "#this" in Moles delegates

When I set a property of a moled type, it looks like they always require, as the first parameter, an object of the original moled type. I also noticed that some of the examples in the Moles Reference Guide assign this parameter as #this and I am trying to figure out why.
For instance, the original class looks something like this:
public class ProductDAO
{
internal void Insert(Product product, string id)
{
...
}
}
When I go to mole this method, the property is expecting a delegate whose first parameter is always the type of the moled object, in this case, a ProductDAO object. So in this case, the property is expecting a delegate of:
Action<ProductDAO, Product, string>
So do I always have to provide that moled object as the first parameter of my lambda expression? If so, what's the difference in using a regular variable name versus #this? What does #this mean/do?
MProductDAO.AllInstances.InsertProductString = (dao, product, id) => { };
versus
MProductDAO.AllInstances.InsertProductString = (#this, product, id) => { };

Resources