How does Grails convert String to Enum? - grails

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.

Related

command object no binding property

I am trying to implement some command object validation, but one property of command object is not binding its always return null
Domain class
package ni.sb
class PurchaseOrder implements Serializable {
Date dateCreated
Date dutyDate
String invoiceNumber
BigDecimal balance
String typeOfPurchase
Date lastUpdated
static constraints = {
dutyDate nullable:false, validator: { dutyDate ->
def today = new Date()
if (dutyDate <= today) {
"notMatch"
}
}
invoiceNumber blank:false, unique:true
balance nullable:true
typeOfPurchase inList:["Contado", "Credito"], maxSize:255
}
}
This is the command object
class PurchaseOrderCommand implements Serializable {
Date dutyDate
String invoiceNumber
String typeOfPurchase
static constraints = {
importFrom PurchaseOrder
}
}
Here is the controller action
def actName(PurchaseOrderCommand cmd) {
if (cmd.hasErrors()) {
println params.dump()
println cmd.dump()
return
}
}
dutyDate is not binding, after i try dumb() in params and cmd i get this
snippet params.dump()
dutyDate:2014-09-25
snippet cmd.dump()
dutyDate=null
I hope you can help me
If you inspect cmd.errors I expect you will see the error there.
If your date request parameters are formatted like "2014-09-25" and you are using a recent version of Grails then something like this should work:
import org.grails.databinding.BindingFormat
class PurchaseOrderCommand implements Serializable {
#BindingFormat('yyyy-MM-dd')
Date dutyDate
String invoiceNumber
String typeOfPurchase
}
Alternatively you could set "yyyy-MM-dd" as one of the default formats in Config.groovy.
// grails-app/conf/Config.groovy
grails.databinding.dateFormats = ['yyyy-MM-dd',
'MMddyyyy',
'yyyy-MM-dd HH:mm:ss.S',
"yyyy-MM-dd'T'hh:mm:ss'Z'"]
// ...
I hope that helps.

enum validation succeeds but should fail

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?

combining criteria from params array

This example here is from the grails docs:
def emeaCriteria = {
eq "region", "EMEA"
}
def results = Airport.withCriteria {
emeaCriteria.delegate = delegate
emeaCriteria()
flights {
like "number", "BA%"
}
}
My webpage is passing back a checkbox group of ethnicities, returning the row ids. So what the server gets is:
ethnicity:[1, 4]
or if the user only picks one ethnicity:
ethnicity:4
def criteria = { params ->
//handle case where only one ethnicity is returned as just a string, not a list of strings
def list = params.ethnicty instanceof String ? [params.ethnicty] : params.ethnicity
if (list) {
inList('ethnicity', list)
}
}
I'm getting an error: java.lang.String cannot be cast to java.lang.Enum.
If I didn't have a list I think I could figure it out. The params are sending back string values, and they need to be converted to the enum class. But within the closure, how do you convert each entry into a list to the enum?
I figured it out through a combination of multiple website posts and with help from dmahapatro above.
def genderCriteria = {
if (params.gender) {
inList('gender', params.list('gender').collect { Gender.valueOf(it)} )
}
}
If a webpage passes back one or more enums (a single string or a list of strings) and you want criteria to check values from the list passed back, you have to provide a list of enum types (not strings or ints).
Here is my enum class for reference:
public enum Gender {
M('Male'),
F('Female'),
U('Unknown')
final String value
Gender(String value) {
this.value = value
}
public String toString() {
value
}
public String getKey() {
name()
}
public String getValue() {
value
}
}
And my criteria builder:
def c = MyDomain.createCriteria()
results = c.list {
genderCriteria.delegate = delegate
genderCriteria(params)
}
Even if no values are passed back for the gender field, it still works (because of the if statement in genderCriteria.
It may not be the best or cleanest solution, but it does work.

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"
}

creating a class for something trivial ?

A Enum is coming back from the service layer with 1 out of 4 options and am using a case statement to handle it in my web code. I thought that I will be doing this at several places and to have some design pattern in place. Now based on each value from Enum I am doing is returning a string . creating a class for each enum seems to be a overkill. what is the best way to handle this
You don't need to create a new subclass for each enum value, you can have multiple instances of the same class, one for each value:
public class MyEnumType {
private readonly string value;
private MyEnumType(string value) {
this.value = value;
}
public string Value {
get { return value; }
}
public static readonly MyEnumType ValueA = new MyEnumType("foo");
public static readonly MyEnumType ValueB = new MyEnumType("bar");
...
}

Resources