Grails 3.3.2 accessing custom meta constraints - grails

In a grails 2.4.4 project, I was able to define my own custom constraint (called 'supportsToUrl') on a domain property and use it as a tag to control rendering logic in my GSP.
GSP rendering code:
if(domainClass.constraints[p.name].getMetaConstraintValue('supportsToUrl'))
Domain class constraint:
static constraints = {
embedCode(nullable:true, blank:true, unique:false, display:true, supportsToUrl:true)
}
In Upgrading from Grails 3.2.x in section "Grails Validator and ConstrainedProperty API Deprecated" there is discussion about how this functionality has been moved. However, I did not see anything in the new API that refers to meta constraints.
My question is: How do I access custom constraints in Grails 3.3.2?

You could access meta constraints still in Grails 3.3.*
From Validateable trait getConstraintsMap().
Example list of all properties which supports url (supportsToUrl: true)
Set<String> supportsUrlProperties = new HashSet<>()
Map<String, Constrained> constraints = domainObject.getConstraintsMap()
if(constraints) {
constraints.values().each { Constrained constrained ->
DefaultConstrainedProperty propertyToCheck = constrained.properties?.property as DefaultConstrainedProperty
if(propertyToCheck) {
def supportsToUrlConstraint = propertyToCheck.getMetaConstraintValue('supportsToUrl')
if (supportsToUrlConstraint != null && BooleanUtils.isTrue(supportsToUrlConstraint as Boolean)) {
supportsUrlProperties.add(propertyToCheck.getPropertyName())
}
}
}
}
Be aware, that it sees only constraints from domain/entity (abstract or not) which are marked with this Validateable trait. Class hierarchy won't apply - when root/super class implements it, then top class's constraints still are not visible, until you mark it also as Validateable.

So based on the ConstrainedDelegate class, I think the short answer is that it's not possible. ConstrainedDelegate does not expose the metaConstraints map or the attributes map of DefaultConstrainedProperty. I will leave the question open though in the hopes that someone more knowledgeable with the Grails architecture roadmap can explain why.
In the meantime, I was able to hack together a solution by re-purposing the format constraint and comparing the format against my predefined tags. Although I'd love to hear other ideas on how to achieve my original goal as this is pretty clearly not how format was intended to be used.

Related

Basic business logic implementation for Grails newbies:

I've been looking at several Grails sample apps, tutorials as well as books (Grails 2 QuickStart) trying to find out how business logic is implemented in Grails. Coming from a railsbackground I expect to see this stuff in the model, but all I'm seeing in the various examples is constraints and such.
Below is a prototype "Account" object with a balance and overdrawn property and a makeDeposit and makeWithdrawal method. It demonstrates all the sort of questions I have. (comments inline).
All of these things are not hard to do in Rails but since none of the books examples I've seen have these kind of methods in the Domain classes perhaps Domain Classes are not meant to have a lot of custom code in them. Would appreciate an example of how to properly implement something like this as a Grails Domain class (or however youre supposed to enfoce rules like these.
class Account {
List deposits
List withdrawals
public long balance = 0;
//is setting a default value the right way to initialize
// this value to 0 in the db?
private Date accountCreated;
//Does private modifer mean accountCreated won't get persisted to the db?
//Assuming I want it in the db, where do initialize this to a client
//supplied value, but make it write-once? I gather I need to have
//parameterless-constructor, so I can't supply it with
//"new Account(creationDate) as you might do in Java.
public boolean isOverdrawn = false;
private void setIsOverdrawn() {}
//does the private setIsOverdrawn method below make this a readOnly to
//other objects in my application?
static hasMany = [deposits: Long, withdrawals: Long]
static constraints = {
balance nullable: false
accountCreated() //if I do this, does that turn this from a
// field into a column?
}
private void setBalance(long newBalance) {
//will the private modifier prevent other objects
//from setting balance directly?
this.balance = newBalance;
if (balance < 0) this.isOverdrawn = true else this.isOverdrawn = false;
}
//this should be the only way for clients to change the balanace in the account.
public void makeDeposit(Long amount) {
deposits.add(amount)
setBalance(this.balance += amount);
this.save()
}
public void makeWithdrawal(Long amount) {
withdrawals.add(amount);
setBalance(this.balance -= amount)
this.save();
}
}
Okay, so you have a larger question of "Where should I put my business logic?" and then also many other questions within your example domain class.
Let's start with the bigger question of "Where should I put my business logic?". There are two choices you can make here. First, as you have already demonstrated you can place the business logic inside the domain class itself. This isn't very common in Grails as the domain classes typically are more of an anemic domain class. In order to do this effectively you have to really understand how GORM and data binding works in order to accomplish quite a few things. As always reading the entire Grails documentation will help you understand how Grails expects things to be done.
The most common approach is to place all your business logic inside services in a service layer. These singleton classes are responsible for accessing and coridinating your domain classes to work as a cohesive system. Again, the Grails documentation on services covers this in theory. The documentation even states:
Services in Grails are the place to put the majority of the logic in
your application...
Now you do have a few minor questions within your example. Overall you have a clear misunderstanding of how GORM and domain classes work within Grails. Public vs Private has no effect within a domain class. In order to make something read only you need to look at hooking into the hibernate events for beforeInsert and beforeUpdate and perhaps even transients. I recommend the GORM section of the Grails documentation for further information.
Since you have experience with Rails you need to try and stop comparing the two. Grails may share some things in common but it has it's own approach. Reading the documentation will help you spot those differences in approach. (Yes, I know it's a lot of reading but it's well written and has a lot of really good information.)

Grails: Apply a custom constraint to all fields of an Domain class

In Grails it's possible to define global constraints within the Config.groovy file which can be used to validate all defined constraints from every domain class using
Config.groovy:
grails.gorm.default.constraints = {
'*'(nullable: true)
...
}
Question:
Is it also possible to define a constraint which is only used for the fields of one domain class? I'm thinking about something like this (which isn't working in reality)
class myDomainClass{
fields
...
static constraints = {
'*'(MyCustomCOnstraint:true)
}
}
I don't know if there is a standard solution to that.
In case there is not, you can build a loop inside the constraint closure:
static constraints = {
// use MyCustomCOnstraint:true for fields firstname, lastname and address
['firstname', 'lastname', 'address'].each { property ->
"$property"(MyCustomCOnstraint:true)
}
}
A few things you could take a look at :
a hack for backward compatibility - since grails 2.3.4 I think : in your config.groovy
// Null object error fix for backward compatibility
grails.databinding.convertEmptyStringsToNull = false
This stops converting blanks to nulls - and may cure your issue, there was a reason why this feature was added - for your own app security... so choose wisely.
You could take a look at programming Grails by Burt Beckwith chapter 3 covers custom validation, from install-templates to making extreme custom validation:
import com.myapp.MyUserValidator beans = {
'com.myapp.UserValidator'(MyUserValidator) }
But to be honest I really don't think there is anything is that segment that could help besides the bit that discusses setting up a filter to convert all input blanks to nulls:
convertBlanksToNullsAndTrim
You could reverse that code so that if it is null make it =''. again with the above point in place if is as per default it could get set as null unless point 1 is set in your config.groovy
If it were me I would try the first option to see if it cures current situation

Difference between two action signatures in grails

In grails we can define an action using 2 ways:-
def actionname()
{
}
and
def actionname = {
}
What is the difference between the two styles? When I tried to insert a spring security annotation above the action (second style) it said "#Secured" not applicable to field.
What does this mean? Is it because of closure?
The Grails reference docs 7.The Web Layer mentions the use of closures for controller actions in earlier versions of Grails, the preference is now to use methods. However, both are supported. It goes on to list some benefits of using methods instead.
Personally, I use methods in all my controllers and have also come across the issue with annotations such as #Secured that only work on methods and not the closures.
In earlier versions of Grails option 2 (actions as closures) was the only supported style. Grails 2.x introduced the actions-as-methods style and this is now the recommended approach, but the closure style is still supported for backwards compatibility and you'll see it if you are working on an app (or plugin) that was originally written on Grails 1.x.
The first is normal method definition with undefined return type.
The second is an assigment of a closure to a property 'actionname'.
That is why you get "#Secured" not applicable to field message, because this annotation is for methods only.
Shuttsy is correct that that the first way is now a preffered way to define actions in Grails.
This is an agreeable way of defining methods in Groovy at minimal level having the structure above.
The second way of defining is not refering to a method definition rather it's somehow a rule like closure constraints that governs a given class or method. Like when it is used in domain class .
Example
class Person {
String name
Person parent
static belongsTo = [ supervisor: Person ]
static mappedBy = [ supervisor: "none", parent: "none" ]
static constraints = { supervisor nullable: true }
//this allowed for methods only and why you got an error in above cases
#override
def toString(){
return name.toString()
}
}
#Secured annotation accept list of roles (String[])
it is used only for a method definition based on
toString() method inside the class ...i just give u a scenario of the two ..
#Secured annotation since spring security 2.0 supports method only. As a result, you have to convert closures to real methods if you want to apply the security annotation for it. Read more #Secured Annotation.

What widget constraints are valid for Grails domain classes?

Can you tell me the list of valid values for the widget constraint below (some-widget), e.g.:
static constraints = {
someField(widget: 'some-widget')
}
The documentation seems to be missing. Related, do you know of any plugins that can work directly with this constraint?
You can have a look there
It's an old list, but it's still valid, I think
From what I can tell, the widget property is only used for scaffolding and is referenced in the renderEditor.template. In that file, it appears that the widget property has some pretty narrow uses depending on the type of object you are scaffolding.
The good news, however, is that you can supply your own renderEditor.template file and edit it to work however you want. Just create a file at ${basedir}/src/templates/scaffolding/renderEditor.template and Grails will pick it up when you generate the views.
(See DefaultGrailsTemplateGenerator for more information.)

Distinguishing between Grails domain-class fields and getBlah() methods via GrailsDomainClassProperty

I'm writing a Groovy script (as part of a Grails plugin) and I want to get a list of properties for a GrailsDomainClass that a user of my plugin might define. I can do this using domainClass.properties (where domainClass is a GrailsDomainClass).
However, suppose a user has the grails domain class:
class Example {
String name
static constraints = {
}
def getSomeNonExistingProperty(){
return "Not-a-real-property"
}
}
In this case, domainClass.properties returns a list with both name and someNoneExistingProperty
I understand that this is because of Grails is generating a read-only property on-the-fly for use where someone has a getBlah() method. That's great, but in my script I want to perform some actions with the "real" properties only (or at least non read-only properties).
That is, I would like some way of distinguishing or identifying someNonExistingProperty as a read-only property, or, alternatively, as a property generated by Grails and not entered explicitly as a field in the domainClass by the user of my plugin.
I've looked at the GrailsDomainClassProperty Class and it has a range of methods providing information about the property. However, none of them appear to tell me whether a property is read-only or not, or to allow me to distinguish between a field defined in the domainClass and a field created on-the-fly by Grails as a result of a "getSomeNonExistingProperty()" method.
Am I missing something obvious here? Is there a way of getting a list of just the explicitly user-defined fields (eg name, in the above example)?
I believe transient properties are what you are trying to exclude
I've run into this problem a few times, and instead of trying to work around it I typically just end up renaming my getX() method. It's probably the easiest option.
Edit:
Alternatively, I wonder if you could use reflection to see which methods are defined on the class, and while iterating over your properties see if the property has an explicit getter defined, and omit it. I'm not very familiar with reflection when it comes to Groovy and Grails, especially with the dynamic methods, but it's a possible route of investigation.

Resources