Grails validate fields with default values - validation

We have a class like this in a Grails 2.4.3 application (migrated from 2.3.8):
#Validateable
class Foo {
Integer noDefault;
Integer withDefault = 1;
static constraints = {
noDefault(nullable:false)
withDefault(nullable:false)
}
}
This class is being instantiated in a complex configuration mechanism using a Map like this:
[
noDefault: 0,
withDefault: 2
]
(In fact the Map is part of a huge one, but the class constructor sees this small one.) Formerly the class worked if we omitted the withDefault entry from the config map, using the default value which is not null. In Grails 2.4.3, however, it tells me that this field cannot be null. I can fix it by letting it be null in the constraint, but it lets setting the explicite value null (and overwrite the default value), which causes problem during operation.
Do you know some workaround, which preserves the semantics and correct operation?
Thanx in advance, best regards: Balázs

What you are describing is not consistent with what I would expect and not consistent with the behavior I am seeing. The project at https://github.com/jeffbrown/validatedefaults contains the following code.
At https://github.com/jeffbrown/validatedefaults/blob/master/src/groovy/demo/Foo.groovy
// src/groovy/demo/Foo.groovy
package demo
import grails.validation.Validateable
#Validateable
class Foo {
Integer noDefault;
Integer withDefault = 1;
static constraints = {
noDefault(nullable:false)
withDefault(nullable:false)
}
}
The test at https://github.com/jeffbrown/validatedefaults/blob/master/test/unit/demo/FooSpec.groovy passes:
// test/unit/demo/FooSpec.groovy
package demo
import spock.lang.Specification
import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
#TestMixin(GrailsUnitTestMixin)
class FooSpec extends Specification {
void 'test validating default values'() {
given:
def map = [noDefault: 0]
def foo = new Foo(map)
expect:
foo.validate()
}
}
When I run the app I get the same behavior.
// grails-app/conf/BootStrap.groovy
import demo.Foo
class BootStrap {
def init = { servletContext ->
def map = [noDefault: 0]
def foo = new Foo(map)
// this prints true...
println "Foo is valid? : ${foo.validate()}"
}
def destroy = {
}
}
I hope that helps.

Related

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).

Bind ItemViewModel to domain class list

I have a Rulebook that contains Rules:
class Rulebook(val rules:MutableList<Rule>)
I have an ItemViewModel for it, as it's used in a multiply-nested selection UI.
class RulebookModel : ItemViewModel<Rulebook> {
val rulesProperty = bind // ... here's my problem
}
What is the correct binding to be able to initialize a tableview with the property?
A naive bind yields the wrong type:
val rulesProperty = bind(Rulebook::rules)
has type Property<MutableList<Rule>>, which tableview() doesn't take.
From another answer here I got Link
val rulesProperty = bind(Rulebook::rules) as ListProperty<Rule>
This yields the correct type, so we get through compilation, but at runtime I get this:
java.lang.ClassCastException: java.util.ArrayList cannot be cast to javafx.collections.ObservableList
Note: The RulebookModel does start life without an item in it yet. I've seen ArrayLists come from empty list factory calls before. Is that possibly my actual problem?
What is the correct way to perform this binding?
Your model needs to have a SimpleListProperty to bind into an itemViewModel
Here is some sample code for how to write the classes and a table view:
data class rule(val name: String, val def: String)
class RuleBookModel{
val rulesProperty = SimpleListProperty<rule>()
var rules by rulesProperty
}
class RuleBookViewModel: ItemViewModel<RuleBookModel>() {
val rules = bind(ruleBook::rulesProperty)
}
class TestView : View("Test View") {
val myRuleBook: RuleBookViewModel by inject()
init {
// adding a rule so the table doesn't look lonely
myRuleBook.rules.value.add(rule("test", "fuga"))
}
val name = textfield()
val definition = textfield()
override val root = vbox{
hbox {
label("Name")
add(name)
}
hbox {
label("Definition")
add(definition)
}
button("Add a rule").action{
myRuleBook.rules.value.add(rule(name.text, definition.text))
}
tableview(myRuleBook.rules) {
column("name", rule::name)
column("def", rule::def)
}
}
}

Implicit not found on store

I have followed the pattern from examples on GitHub. When I call store on the model object, passing an instance of the entity, I get a compile error indicating one of the implicit parameters is missing as shown below.
could not find implicit value for parameter sg: com.outworkers.phantom.macros.SingleGeneric.Aux[com.ss.wuhu.settlement.entity.Settlement,Repr,HL,Out]
I guess I am missing something obvious. Could someone please point out how to bring the implicit into scope?
Regards
Meeraj
This is the code snippet where I am storing the data.
import akka.Done
import com.outworkers.phantom.dsl._
import com.outworkers.phantom.connectors.{CassandraConnection, ContactPoints}
import com.ss.wuhu.settlement.entity.Settlement
import com.ss.wuhu.settlement.entity.mapping.{SettlementForCourierModel, SettlementForVendorModel}
object Connector {
private val hosts = Seq("127.0.0.1") // TODO from environment
lazy val connector: CassandraConnection = ContactPoints(hosts).keySpace("wuhu_order")
}
class SettlementDatabase(override val connector: CassandraConnection) extends Database[SettlementDatabase](connector) {
object SettlementForCourierModel extends SettlementForCourierModel with connector.Connector
object SettlementForVendorModel extends SettlementForVendorModel with connector.Connector
def truncateAll() = {
Database.truncate()
}
def store(set: Settlement) = {
for {
v <- Database.SettlementForVendorModel.store(set)
d <- Database.SettlementForCourierModel.store(set)
} yield (Done)
}
}
object Database extends SettlementDatabase(Connector.connector)
This is a known bug with an open issue: https://github.com/outworkers/phantom/issues/774
I suggest either using the workaround as described in the link above, or my workaround which was creating my own .store() using .insert().
example:
def myStore(person: Person) : Future[ResultSet] =
insert
.value(_.name, person.name)
.value(_.age, person.age)
.value(_.timeCreate, person.timeCreate)
.future()

Returning object of type specified in method arguments instead of AnyRef

I have the following method:
#org.springframework.stereotype.Service
class EntityCacheManager {
def get(cacheId: String, entityClass: Class[_]): AnyRef = { ... }
//...
}
So to use it, i have to write this:
val cachedEntity = entityCacheManager.get(cacheId, classOf[SomeEntity]).asInstanceOf[SomeEntity]
Is there some way to make EntityCacheManager.get() returning instance of type entityClass which is specified in method params? I'd like to avoid casting asInstanceOf every time i use this method. I know it would be nice to use generic definition of type EntityCacheManager, but it's also a spring-managed bean, so i think using generics will cause troubles.
You can use a more idiomatic scala approach by using the ClassTag typeclass
class EntityCacheManager {
def get[T: ClassTag](cacheId: String): T = {
val entityClass = implicitly[ClassTag[T]].runtimeClass
val myObject: T = ??? // you retrieve your object somehow using entityClass
myObject
}
}
you can now use it like this:
val myEntityClassInstance = get[MyEntityClass]("key")

Grails - Field error disappears from parent class

I have the following problem:
I have a domain class called customer, with a field discount embedded.
class Customer {
...
String username
Discount discount
static constraints = { ... }
}
class Discount {
Integer item1
Integer item2
static constraints = { item1 min:1, max:100, nullable:true }
}
I have a controller, where a customer's data can be modified. The code goes something like this:
def edit() {
Customer c = Customer.findByUsername(params.userName)
if(request.method != 'GET'){
bindData(c, params)
if(c.validate()) {
//save the result
}
}
println c.dump()//1
model:[customer:c]
}
Then in the edit.gsp I put the following code:
${customer.dump()}//2
${customer.discount.dump()}
Now my problem is, if I have a validation error, for example the user enters 123 for item1, I get the appropriate errors object which says that Customer bean has 1 field error on field discount.item1 when I call println c.dump()//1
In the edit.gsp on the other hand, the customer bean doesn't have any field errors but customer.discount has the mentioned error. This is a big inconvenience, because I want to render errors next to the fields like so:
<g:renderErrors bean="${customer}" field="discount.item1"/>
But the customer bean doesn't have any errors, just the discount bean (therefore I don't get any errors rendered).
Has this problem occured to any of you ?
It seems that #Validatable classes use functionality of spring framework's AbstractBindingResult, which doesn't support this usage (as far as I can tell).
However I was able to create a workaround, which could be used as a taglib to achieve the same effect:
Given two validateable classes
#Validateable
class TestA {
TestB b
static constraints = {
b validator: {
it.validate()
}
}
}
#Validateable
class TestB {
int c
static constraints = {
c min: 100
}
}
One can simply resolve the bean and the attribute and delegate the rendering to the existing taglib.
import test.*
import org.codehaus.groovy.grails.plugins.web.taglib.*
ValidationTagLib validationTagLib = ctx.getBean(ValidationTagLib)
renderErrorsWithNestedFields = { attrs, body ->
def modifiedAttrs = attrs
String[] path = attrs?.field?.split('\\.')
if(path?.size() > 1) {
Object resolvedBean
resolvedBean = attrs.bean
path[0..-2].each {
//Some error handling would not hurt, but this is just a proof of concept
resolvedBean = resolvedBean[it]
}
modifiedAttrs = new HashMap(attrs)
modifiedAttrs.bean = resolvedBean
modifiedAttrs.field = path.last()
}
validationTagLib.renderErrors (modifiedAttrs, null)
}
TestA a = new TestA(b:new TestB(c:3))
a.validate()
renderErrorsWithNestedFields([bean:a, field:'b.c'], null)
And this should render an error for violating the min constraint on c.

Resources