geofflane / grails-constraints Goto Github PK
View Code? Open in Web Editor NEWCustom constraints plugin for Grails applications
License: Apache License 2.0
Custom constraints plugin for Grails applications
License: Apache License 2.0
I have created a simple grails-app that demonstrates that when the constraints plug-in is installed that my integration test fails and without it, it succeeds. The failure I get is specifically
No signature of method: net.rrullo.tester.UserController$_closure9.doCall() is applicable for
argument types: (net.rrullo.tester.UserCommand, net.rrullo.tester.UserCommand) values:
[net.rrullo.tester.UserCommand@1c1902d, net.rrullo.tester.UserCommand@6c4fe] Possible
solutions: doCall(net.rrullo.tester.UserCommand), call(), call(net.rrullo.tester.UserCommand),
call([Ljava.lang.Object;), call(java.lang.Object)
I'm new to publishing to github so I'm not sure if this is the appropriate way to do this or not, but my demo testapp is available at http://dl.dropbox.com/u/6783272/constraints-test.zip.
Thanks!
-Bob
When writing a custom grails constraint, the validation might fail for multiple reasons and you can instruct grails to use a different error message code in each case. For example, a ZIP code validator might fail because the ZIP is the wrong length, or the ZIP doesn't exist.
Grails also allows you to specify custom arguments that will be used when those messages are resolved. Here's an example of how to use both these features with a Grails custom validator:
zipCode(validator: {zipCode, target ->
def zipCodeLength = 5
if (zipCode?.size() != zipCodeLength) {
// In this case the message code "className.propertyName.size" will be used and zipCodeLength will be passed
// as the message argument with index 3
return ['size', zipCodeLength]
}
if (!target.locationManager.findZipCode(zipCode)) {
// In this case the message code "className.propertyName.invalid" will be used
return 'invalid'
}
})
It seems that the failureCode property allows you to use a different message for each class that uses a constraint. However, for a given class it seems you're limited to one error message and there's no way to pass custom arguments when the message is resolved.
BTW, thanks a lot for the plugin, seems to have been very well thought out.
Just did a simple install into grails 1.3.7. Ran app and got the following errors
2011-05-19 11:15:29,979 [main] ERROR commons.DefaultGrailsApplication - The class [net.zorched.constraints.ComparisonConstraint] was not found when attempting to load Grails application. Skipping.
2011-05-19 11:15:29,984 [main] ERROR commons.DefaultGrailsApplication - The class [net.zorched.constraints.SsnConstraint] was not found when attempting to load Grails application. Skipping.
2011-05-19 11:15:29,988 [main] ERROR commons.DefaultGrailsApplication - The class [net.zorched.constraints.UsPhoneConstraint] was not found when attempting to load Grails application. Skipping.
2011-05-19 11:15:29,992 [main] ERROR commons.DefaultGrailsApplication - The class [net.zorched.constraints.UsZipConstraint] was not found when attempting to load Grails application. Skipping.
Causes console to crash whenever a domain with a constraint is modified.
Tried uninstalling and reinstalling, same issue.
Would be cool to get people started - and we could start submitting a whole collection of them and really build it up.
Is it possible to have different constraint sets for the same Domain object, that can be applied
in different situations? I.e. to have not only one but several?
I see many examples how to "share" constrains, but I need exactly the opposite:
e.g. on the same Domain, under different circumstances to change the behavior.
I am using grails 2.3.7 and trying to use IBM JVM .The application fails on start up because of the constraint plugin.My logs on startup is
Fatal error during compilation java.lang.ClassFormatError: JVMCFRE009 interface field must be public static and final; class=net/zorched/grails/plugins/validation/GrailsConstraintClass, offset=1396 (NOTE: Stack trace has been filtered. Use --verbose to see entire trace.)
Has anyone faced this?
Hi Geoff,
Pull request #15 offers support for @TestMixin annotations in Grails 2 unit tests for Constraints.
There is another feature that can expand this even more, and that's supporting the @testfor annotation outright. This would mean instead of this:
@TestMixin(ConstraintUnitTestMixin)
class MyConstraintTests {
void testStuff() {
def constraint = testFor(MyConstraint)
// Params are automatically mixed in to the test class and exposed
// to the constraint with the call above.
params.foo = "bar"
assert constraint.validate("foo", null, null) == true
}
}
You can do this:
@TestFor(MyConstraint)
class MyConstraintTests {
void testStuff() {
// No need to setup constraint, it's automatically setup in each test.
// Params are automatically mixed in to the test class and exposed
// to the constraint with the call above.
params.foo = "bar"
assert constraint.validate("foo", null, null) == true
}
}
@testfor handles weaving in the relevant mixin class, so in the latter example ConstraintUnitTestMixin would still get mixed into the class at compile time. TestFor has the added avantage of automatically calling testForโ to setup the class under test in each test method. Sicne this is now the standard in Grails for testing core artefacts like Services/Controllers/Taglibs, it is nice to offer this for custom artefacts like Constraints too.
The caveat is to offer this support a small amount of monkey-patching is required. A hook needs to be added to _Events.groovy to let TestForTransformation know about your custom artefact. Take a look at how I did it in my of my plugins and you'll see what I'm talking about.
I posted about this on the Grails user mailing list and Graeme Rocher responded without any complaints about this approach. He recommended a feature request be raised to have an official API for this in a future version of Grails 2.
So it's up to you really, if you want to add in the small amount of monkey-patching code to _Events.groovy then you can have @testfor support now, or you can wait until there's an official mechanism to register custom artefacts.
Let me know what you think!
โ well actually, TestForTransformation automatically calls mock, testFor is just a convention and in the ConstraintUnitTestMixin it just calls mockConstraint
I wanted a constraint like the one below. But i always get errors like this:
Property [null] with value [12/10/10 12:00 AM] is not [>] [releaseDate]
Given this message:
default.invalid.compare.message=Property [{0}] with value [{1}] is not [{2}] [{3}]
/**
def validate = { val, target, errors ->
def compareVal = target."$params.field"
def result = false
if (val && compareVal) {
switch (params.op) {
case ">": result = val.compareTo(compareVal) > 0; break
case ">=": result = val.compareTo(compareVal) >= 0; break
case "==": result = val.compareTo(compareVal) == 0; break
case "<=": result = val.compareTo(compareVal) <= 0; break
case "<": result = val.compareTo(compareVal) < 0; break
default: throw new IllegalArgumentException("Invalid operation: " + params.op)
}
}
if (!result) {
//TODO: constraintPropertyName doesn't seem to get set? not sure why not.
def args = ArrayUtil.createArray(constraintPropertyName, val, params.op, params.field)
errors.rejectValue(constraintPropertyName, 'default.invalid.compare.message', args, 'Invalid value')
}
return true //always return true, since we are manually inserting the error into the errors obj
}
}
Hi, I have a domain class with nullable: true in a property and then a domain custom constraint in the same property, the problem is that the custom constraint is not executing when nullable is true, if I set nullable to false, the custom constraint executes normally.
I've been reading and this could be happening because nullable is vetoing other constraints, this issue has been solved in grails, but seems that it wasn't in this plugin.
thanks!
Thank you for creating this great plugin.
I have uninstalled hibernate plugin because I wanted to use only MongoDB for the data source.
When I installed the custom constraints plugin, a compilation error occurred.
The error message said that "DefaultGrailsConstraintClass.java" has a dependency to hibernate plugin(org.hibernate.SessionFactory).
Are there any ways to avoid this error?
I would prefer to have my constraints more of a higher-level artifact, putting them in grails-app/Constraints
but there isn't an easy way to do this right now. This ability is also very much needed if other plugins are looking to bundle in custom constraints that use this plugin. (for example, shared domain classes between projects)
I have created a unit test and mocked a domain class and am not seeing my custom constraint being caught.
I'm using mockDomain() to create and work with my domain class.
I've created a test case that demonstrates the problem within 2 classes http://dl.dropbox.com/u/6783272/constraints-mock.zip.
Just run test-app and the errors demonstrate the problem.
-Bob
I have been writing a custom constraint, but it was obvious that the validate method was not getting called when null or blank values were passed to it.
I had to resort to digging through the code to see if I could work out why this was. I found out that I could get the validate method to fire by having these two lines in my constraint:
def skipBlankValues = false
def skipNullValues = false
If this could be mentioned in the docs it would have saved me a lot of time!
In the plugin documentation, it indicates that dependency injection of services is supported. The following code doesn't seem to inject the armService into the constraint.
package com.vitalsmarts.ema.constraint
class OrderFoundConstraint {
def armService
static defaultMessageCode = "default.order.not.found.error"
static failureCode = "order.not.found.error"
def validate = {identifier ->
armService.basicOrderInfo(identifier) ? true : false
}
}
The exception is a null pointer because armService is null. Any suggestions oralternatives?
Just trying up upgrade my project to Grails 2.0 and run into a problem with this plugin.
Some of my domain classes share a need for complicated validation, so I use your plugin to extract it to a common constraint. However when running my unit tests in Grails 2.0 I get:
java.lang.IllegalArgumentException: ServletContext must not be null
When I attempt to construct a Domain class with this custom constraint. If I comment out the constraint, everything works fine.
Steps:
grails create-constraint net.rrullo.tester.FooBar
Creates FooBar in package structure net.rrullo.tester correctly, but does not place a package name in the groovy class.
Fix is simple, update the /src/templates/artifacts/Constraint.groovy from
class @artifact.name@ {
def validate = { propertyValue ->
// execute validation
}
}
@artifact.package@class @artifact.name@ {
def validate = { propertyValue ->
// execute validation
}
}
I installed the plugin. I ran the create-constraint script and created my constraint. I set the constraint on each property in my domain class. Then I tried to use the constraint in my 'create' template with the following line:
<% if(!cp?.secondary) { %>
And I get the following error when trying to generate views:
Error executing script GenerateViews: No such property: secondary for class: org.codehaus.groovy.grails.validation.ConstrainedProperty
No such property: secondary for class: org.codehaus.groovy.grails.validation.ConstrainedProperty
I'm really not sure why it doesn't recognize the constraint. I'm not trying to do anything overly complicated.
The docs here:
https://github.com/geofflane/grails-constraints/blob/master/README.markdown#defaultmessagecode-property-optional
state that "The default value is default.$name.invalid.message". However, it appears that the actual default value of the defaultMessageCode is default.invalid.$name.message ($name and invalid are in different orders). However, I don't think the fix should be to change the docs. To be consistent with the rest of grails (look at the default messages for the built-in grails constraints as well as the message codes defined for custom constraints by this plugin), I believe the code should be fixed to reflect what the docs say. It looks like the DefaultGrailsConstraintClass is the problem:
public String getDefaultMessageCode() {
String obj = (String)getPropertyValue(DEFAULT_MESSAGE_CODE_PROPERTY);
if (obj == null) return "default.invalid." + getConstraintName() + ".message";
return obj;
}
This line:
if (obj == null) return "default.invalid." + getConstraintName() + ".message";
should change to read:
if (obj == null) return "default." + getConstraintName() + ".invalid.message";
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.