2015-01-26 Tweets

Issue With Groovy Validator

I am having some issues with the Groovy Validator project. Here is an email I sent to the Groovy user mailing list:

I am making some annotations to do validation on POGOs. The github project is here:

https://github.com/emacadie/groovy-validator

One of my annotations is @StringAnnotation, which has the properties minLength, maxLength and regEx. I also have annotations for ints, longs, floats and doubles.

I got them working with POGOs and POGOs that have the @Immutable annotation.

So far I have the annotations working for POGOs by calling a static method on a class called AnnotationProcessor, and the annotations are handled by @Immutable objects with AstImmutableConstructorTransform.

https://github.com/emacadie/groovy-validator/blob/proper_annotations/src/main/groovy/info/shelfunit/properties/annotations/AnnotationProcessor.groovy

https://github.com/emacadie/groovy-validator/blob/proper_annotations/src/main/groovy/info/shelfunit/properties/annotations/AstImmutableConstructorTransform.groovy

I decided I would like to process the individual annotations with their own processor, so I started with the @StringAnnotation.
The AST class is here:

https://github.com/emacadie/groovy-validator/blob/proper_annotations/src/main/groovy/info/shelfunit/properties/annotations/StringAnnotationTransform.groovy

There are a lot of println statements since I am trying to figure all of this stuff out.

The issue that I am having is that when I run some tests the StringAnnotationTransform does not seem to work. All the strings wind up null in my tests.
Here is the command I use:
gradle clean; gradle -Dtest.single=BookSequelTest test -info

Yet, when I try these in the Groovy console (which I run with “gradle console”) things work as expected.

Why does everything work in the console but not in tests? Any ideas?

2015-01-25 Update

I have a bit more free time on my hands these days.

I plan on getting more into Clojure soon. I might use it to build my mail server. Or I might try that with GPars first.

Before that, I will take another crack at Groovy Validator. I would like to get rid of the static class that processes the annotations for mutable objects. I have one way of processing them for immutable objects, and another for mutable. It should be the same to the end developer.

 

2015-01-12 Tweets

2014-12-08 Tweets

Changing Immutable Objects

Recently someone at work someone sent me a link to an article saying that getters and setters are evil by a guy named Yego256. He has a few other interesting posts about immutable objects here and here. They can be added to the list I made of pages about immutable objects here.

Hopefully Grails 3 will utilize immutability.

It takes a shift in mindset to start using immutable objects. Plus, in Java (and Groovy) a lot of web frameworks don’t utilize them, so people are not too familiar with them.

One issue some developers have is: Many things in the world are mutable. How can you make “changes” to immutable objects?

One answer is that you can use the values you don’t want to change to populate a new immutable object, and your variable/field name points to a new immutable object.

(If you can think of a better way to explain this, let me know. It’s kind of confusing to say that an immutable object is referenced by a “variable name”.)

I will demonstrate this with Groovy (using the annotations EqualsAndHashCode, Immutable and ToString). I made a class to model a state in the USA. States do not change their names, abbreviations or year they joined the union. But many of them have changed their capital cities. So we will make an object, and change its capital city while keeping everything else the same.

Here is our class:

package info.shelfunit.states

import groovy.transform.EqualsAndHashCode
import groovy.transform.Immutable 
import groovy.transform.ToString

@Immutable
@ToString( includeNames = true )
@EqualsAndHashCode
class USState {
    String name
    String abbrev
    String capital
    int yearJoined
}

Now let’s look at using it in a Groovy shell. The first capital of Illinois was a town called Kaskaskia, which no longer exists. Then it moved to Vandalia (which does still exist) and finally to Springfield.

I will try to change a few fields to prove the objects are immutable, and I will call hashCode() to show that we are dealing with different objects.

groovy:000> import info.shelfunit.states.*
===> [import info.shelfunit.states.*]
groovy:000> il = new USState( name: 'Illinois', abbrev: 'IL', capital: 'Kaskaskia', yearJoined: 1818)
===> info.shelfunit.states.USState(name:Illinois, abbrev:IL, capital:Kaskaskia, yearJoined:1818)
groovy:000> il.name
===> Illinois
groovy:000> il.name = "Indiana"
ERROR groovy.lang.ReadOnlyPropertyException:
Cannot set readonly property: name for class: info.shelfunit.states.USState
        at info.shelfunit.states.USState.setProperty (USState.groovy)
        at groovysh_evaluate.run (groovysh_evaluate:3)
        ...
groovy:000> il.toString()
===> info.shelfunit.states.USState(name:Illinois, abbrev:IL, capital:Kaskaskia, yearJoined:1818)
groovy:000> il.hashCode()
===> 1673606286
groovy:000> il = new USState( name: il.name, abbrev: il.abbrev, capital: 'Vandalia', yearJoined: il.yearJoined )
===> info.shelfunit.states.USState(name:Illinois, abbrev:IL, capital:Vandalia, yearJoined:1818)
groovy:000> il.abbrev
===> IL
groovy:000> il.abbrev = "WI"
ERROR groovy.lang.ReadOnlyPropertyException:
Cannot set readonly property: abbrev for class: info.shelfunit.states.USState
        at info.shelfunit.states.USState.setProperty (USState.groovy)
        at groovysh_evaluate.run (groovysh_evaluate:3)
        ...
groovy:000> il.toString()
===> info.shelfunit.states.USState(name:Illinois, abbrev:IL, capital:Vandalia, yearJoined:1818)
groovy:000> il.hashCode()
===> -934597459
groovy:000> il = new USState( name: il.name, abbrev: il.abbrev, capital: 'Springfield', yearJoined: il.yearJoined )
===> info.shelfunit.states.USState(name:Illinois, abbrev:IL, capital:Springfield, yearJoined:1818)
groovy:000> il.capital
===> Springfield
groovy:000> il.capital = "Homerville"
ERROR groovy.lang.ReadOnlyPropertyException:
Cannot set readonly property: capital for class: info.shelfunit.states.USState
        at info.shelfunit.states.USState.setProperty (USState.groovy)
        at groovysh_evaluate.run (groovysh_evaluate:3)
        ...
groovy:000> il.toString()
===> info.shelfunit.states.USState(name:Illinois, abbrev:IL, capital:Springfield, yearJoined:1818)
groovy:000> il.hashCode()
===> 213225124
groovy:000>

So we used the existing values to make new objects. The JVM used the same variable name (“il”) to refer to different immutable objects.

Here is the code without the output:

import info.shelfunit.states.*
il = new USState( name: 'Illinois', abbrev: 'IL', capital: 'Kaskaskia', yearJoined: 1818)
il.name // returns "Illinois"
il.name = "Indiana" // will cause groovy.lang.ReadOnlyPropertyException
il.toString()
il.hashCode()

// Change capital to Vandalia
il = new USState( name: il.name, abbrev: il.abbrev, capital: 'Vandalia', yearJoined: il.yearJoined )
il.abbrev // returns "IL"
il.abbrev = "WI" // causes groovy.lang.ReadOnlyPropertyException:
il.toString()
il.hashCode()

// Change capital to Springfield
il = new USState( name: il.name, abbrev: il.abbrev, capital: 'Springfield', yearJoined: il.yearJoined )
il.capital // returns "Springfield"
il.capital = "Homerville" // causes groovy.lang.ReadOnlyPropertyException:
il.toString()
il.hashCode()

You’re welcome.