enum validation succeeds but should fail - grails

In my unit test under grails 2.2.4 I'm attempting to pass in an invalid enum to see if it gets rejected. It does not.
Here is my enum:
public enum CertificationStatus {
N("No"),
Y("Yes - Unverified"),
V("Yes - Verified")
final String value
CertificationStatus(String value) {
this.value = value
}
public String toString() {
value
}
public String getKey() {
name()
}
public String getValue() {
value
}
}
Here is my domain:
class Profile
CertificationStatus certFosterCertified
static constraints = {
certFosterCertified(blank: true, nullable: true)
}
Here is the unit test:
instance = new Profile(certFosterCertified: '#')
assertFalse instance.validate(['certFosterCertified'])
assertNotNull instance.errors.getFieldError('certFosterCertified')
The instance.validate returns true, but I'm passing in an invalid value for the enum in the Profile constructor ('#'). Shouldn't the validate fail because of the invalid enum? The enum is setup with only Y,N, and V as valid values. I didn't think I had to set those in the constraint because the field is defined as an enum.

With the constructor you set the value of certBackgroundCheck to #.
However, in your domain class your enum is named certFosterCertified. So certFosterCertified should be null, because it isn't initialized. According to the constraints null is a valid state (nullable: true).
Maybe you just need to change certBackgroundCheck to certFosterCertified?

Related

Initialization error when creating an object

When I compile the following code:
class Student {
int id;
Student() {
this.id = 12345;
}
}
void main() {
var student1 = new Student();
}
I get the following error:
Error: Field 'id' should be initialized because its type 'int' doesn't
allow null.
But why do I get this error? I did initialize id in the constructor!
In Dart, the creation of objects are split into two phases:
Initialization of all values.
Execution of constructor body.
So when you are running code inside the constructor body (between the {...} in the constructor definition) then all class defined variables must have been provided a default value that is valid for the type of variable.
In your case, the variable is typed int but are not provided a default value. In Dart, all variable will by default be set to null in case of no other value provided. But since int is a non-nullable type it does not allow null to be a value and the compiler are therefore giving you the error.
The solution are to provide a value before the constructor is running. You can do that like this:
class Student {
int id;
Student() : id = 12345;
}
Or:
class Student {
int id = 12345;
Student(); // The constructor can in theory just be removed here
}
In case you cannot define a value as part of the initialization phase, you can (but should be prevented if possible) mark the variable as late which makes it so you promise, the Dart compiler, that you are going to provide a value for the variable before the first time you are trying to read from that variable:
class Student {
late int id;
Student() {
this.id = 12345;
}
}
In case you are trying to read from id before it have been provided a value, the program will crash with a LateInitializationError at runtime.
And at last, you can set the type to be a nullable type, like int?, to allow the variable to have a default value of null. But doing so will require you to check for null when you are trying to do something with the value in a context where null is not allowed:
class Student {
int? id;
Student() {
this.id = 12345;
}
}

Dozer - using custom create method

I am not sure if I understand custom create method in Dozer mapper correctly. I need to translate bean property of type int to TransTypeCodebook object instance. But I am getting:
2013-09-13 15:47:27,009 [main] ERROR org.dozer.MappingProcessor - Field mapping error -->
MapId: null
Type: null
Source parent class: cz.jaksky.dozer.a.HolderA
Source field name: transType
Source field type: class java.lang.Integer
Source field value: 0
Dest parent class: cz.jaksky.dozer.b.HolderB
Dest field name: transTypeCodebook
Dest field type: cz.jaksky.dozer.b.codebook.TransTypeCodebook
org.dozer.MappingException: Illegal object type for the method 'setTransTypeCodebook'.
Expected types:
cz.jaksky.dozer.b.codebook.TransTypeCodebook
Actual types:
java.lang.String
My TransTypeCodebook class
public class TransTypeCodebook extends Codebook {
private int code;
private String label;
private TransTypeCodebook(int code, String label) {
this.code = code;
this.label = label;
}
public int getCode() {
return code;
}
public String getLabel() {
return label;
}
public static TransTypeCodebook getCodebook(int code) {
TransTypeCodebook result;
switch (code) {
case 0:
result = new TransTypeCodebook(0, "Case0");
break;
case 1:
result = new TransTypeCodebook(1, "Case1");
break;
default:
result = new TransTypeCodebook(code, "Not a valid code");
}
return result;
}
}
Mapper portion
<field>
<a>transType</a>
<b create-method="getCodebook">transTypeCodebook</b>
</field>
I manged to solve this issue by custom converters but I am not sure if I understand the concept of custom create method and more over I am wondering from where that String is comming. Can anyone put light on that?
I am not exactly sure, but to use static methods like that, you need to specify its fully qualified name.
<b create-method="your.domain.TransTypeCodebook.getCodebook">transTypeCodebook</b>
That's stated in the Dozer documentation.

Grails custom validater, getting a reference to the field name

I am trying to write a generic custom validator for a property, to make it generic i need a reference to the field name within the closure, the code is as follows
In config.groovy
grails.gorm.default.constraints = {
nameShared(validator: {val, obj, errors->
Pattern p = Pattern.compile("[a-zA-Z0-9-]{1,15}")
Matcher m = p.matcher(val)
boolean b = m.matches()
if(!b)
errors.rejectValue('name', "${obj.class}.name.invalid", "Name is invalid")
})
in My domain class
class Student{
String name
static constraints = {
name(shared:'nameShared')
}
}
class Teacher{
String firstName
String lastName
static constraints = {
firstName(shared:'nameShared')
}
}
I want to use the same validator for both name and firstName, but since i am hardcoding the fieldName in the validator, it will always work for name and not firstName, so i want to know if there is anyway i can get a reference to the fieldname and make the validator generic, please help
You could use the variable propertyName to get the name of the validated property.
From the grails docs:
The Closure also has access to the name of the field that the constraint applies to using propertyName
myField validator: { val, obj -> return propertyName == "myField" }
You could wrap your validator-closure inside another function like this:
def getValidator(myValue) {
return { val, obj, errors ->
// make use of myValue
}
}
myField validator: getValidator('foo')

Enum in Grails Domain

I'm trying to use an enum in a Grails 2.1 domain class. I'm generating the controller and views via the grails generate-all <domain class> command, and when I access the view I get the error shown below. What am I missing here?
Error
Failed to convert property value of type java.lang.String to required type
com.domain.ActionEnum for property action; nested exception is
java.lang.IllegalStateException: Cannot convert value of type
[java.lang.String] to required type [com.domain.ActionEnum] for property action:
no matching editors or conversion strategy found
Enum (in /src/groovy)
package com.domain
enum ActionEnum {
PRE_REGISTER(0), PURCHASE(2)
private final int val
public ActionEnum(int val) {
this.val = val
}
int value() { return value }
}
Domain
package com.domain
class Stat {
ActionEnum action
static mapping = {
version false
}
}
View
<g:select name="action"
from="${com.domain.ActionEnum?.values()}"
keys="${com.domain.ActionEnum.values()*.name()}" required=""
value="${xyzInstance?.action?.name()}"/>
EDIT
Now getting error Property action must be a valid number after changing the following.
View
<g:select optionKey='id' name="action"
from="${com.domain.ActionEnum?.values()}"
required=""
value="${xyzInstance?.action}"/> // I tried simply putting a number here
Enum
package com.domain
enum ActionEnum {
PRE_REGISTER(0), PURCHASE(2)
final int id
public ActionEnum(int id) {
this.id = id
}
int value() { return value }
static ActionEnum byId(int id) {
values().find { it.id == id }
}
}
Domain
package com.domain.site
class Stat {
static belongsTo = Game;
Game game
Integer action
static mapping = {
version false
}
static constraints = {
action inList: ActionEnum.values()*.id
}
String toString() {
return "${action}"
}
}
Take a look here ...
Grails Enum Mapping
Grails GORM & Enums
Also you may be hitting this as well. From the docs:
1) Enum types are now mapped using their String value rather than the ordinal value. You can revert to the old behavior by changing your mapping as follows:
static mapping = {
someEnum enumType:"ordinal"
}

How does Grails convert String to Enum?

I have a custom toString method in my enum:
enum TaxRate implements Serializable {
RATE23(23.0),
...
private String s
private BigDecimal rate
private TaxRate(BigDecimal s) {
this.s = s + "%"
this.rate = s * 0.01
}
public String toString() {
return s
}
Now when I display the rates in HTML I get nice output like TAX: 23.0%.
But what happens when a user selects the tax from a <select> and the sent value is i.e. 23.0% is that Grails can't create/get the TaxRate instance...
What should I override to support this custom mapping? Trying to override valueOf(String) ended with a error..
Have you seen the entry at the bottom of this page?
If you want to use a Enum with a "value" String attribute (a pretty common idiom) in a element, try this:
enum Rating {
G("G"),PG("PG"),PG13("PG-13"),R("R"),NC17("NC-17"),NR("Not Rated")
final String value
Rating(String value) { this.value = value }
String toString() { value }
String getKey() { name() }
}
Then add optionKey="key" to your tag.

Resources