How to create/use shared domain properties/methods in Grails 2? - grails

before Grails 2.x we used an abstract class to model shared domain properties. This worked perfect but now when using Grails 2.x is see no way of creating shared domain properties and methods. When i use an abstract class and let my domain classes extend that abstract class i get one big database table.
Is there a alternative way of creating shared domain properties / methods?

Docs says that you need to move your base class into the /src/groovy at this case

Related

Grails 3.1.1 - Dirty checking not working when model class extends another groovy class

I have a domain class which extends another groovy class with same name but in different package in a different library.
The problem is when I modify instances on the domain class, it is not marked as dirty & hence changes are not persisted.
I have read that grails 3 release has some enhancements to dirty checking & this could be a bug or I am missing something.
New objects are saved properly without any issues, I have used isDirty() method on modified domain object as well as modified properties & both return false. Objects are attached to the session, confirmed with isAttached().
To reproduce, I created a test project with following code & tried updating the object from default grails views that are generated using scaffolding, but still the changes are not persisted.
Note: I have done similar stuff in Grails 2.4 & it used to work.
Domain class is as follows:
package com.perseus
class Derived extends Base{
static constraints = {
name blank: false, nullable: false
}
}
Base class in src/main/groovy:
package com.perseus
class Base implements Serializable {
private static final long serialVersionUID = 1L
String name
}
Controller
package com.perseus
class DerivedController {
static scaffold = Derived
}
Link to github project.
Isssue: Model is not marked dirty, even if it has been modified. This happens when a model class extends another groovy class.
How to reproduce:
Run the app.
Create a new model object (model name is Derived)
Modify the object using edit view & click Update.
You will see that modifications are not persisted.
Finally I found a reference here which explains the reason for failure to update dirty check status.
I added #DirtyCheck annotation and it solved the problem.
However this has a negative impact on the design of our libraries.
We have separate projects for each category:
Business model classes
Business logic classes
User Interface
The idea is to have modules completely independent of each other. So model & business logic liraries can be used by any UI technology. Now the problem is that I had to add gorm dependency to my business model project for the annotation.
compile 'org.grails:grails-datastore-gorm:6.1.7.RELEASE'
Design wise we like our model classes in a project completely independent of UI or persistent technologies like hibernate. So that these model classes can be used in variety of different projects without any additional dependencies. And we achieved this with grails so far by creating a new class that extends actual model class in our library
Is there any way I can solve this problem, without modifying the base class?
In short, model classes (POJO) are now dependent on grails framework, whereas in earlier versions they were not.

Which interface/abstract class is for grails Domain class behaviour?

I have a generic method for doing a common operation on many domain class
static Map getNumberOfPropertyByTopicIds(def criteriaClass, List ids) {
criteriaClass.createCriteria(). //Some GORM methods used
}
I wanted autocomplete on various things applied on criteriaClass. But for doing that I need to replace def criteriaClass to InterfaceForDomainClassBehaviour criteriaClass.
But I don't know InterfaceForDomainClassBehaviour is what. Which interface/abstract class implements Domain class behaviour?
There isn't one.
Grails uses "convention over configuration", so unlike other frameworks where you extend a base class, implement one or more interfaces, use annotations, etc., you simply put your artifact classes (domain classes, services, etc.) in the correct directory under grails-app, use the appropriate class naming convention (except for domain classes), and Grails mixes in behavior for you. You can configure things of course, e.g. with the mapping block, etc.
Before Grails 2 adding methods was mostly done using Groovy runtime metaprogramming, and in Grails 2 most of the behavior is added at compile time using ASTs, and runtime metaprogramming is used mostly for dynamic code like findAllByHeightAndWeightAndHairColorAndShoeSize where it would be impractical and/or impossible to compile in every combination.
Over 100 methods are added to domain classes (decompile some - it's pretty amazing to see how much ends up in your classes considering how small the Groovy source is) and dozens are added to controllers. But this is all mixed in, so although there is significant overlap between your domain classes, there's no common base class or interface unless you add them yourself.

Domain class variables

How do domain classes in Grails have variables like static constraints ={ }?
I can't see any direct inheritance.
I guess it's meta-programming but can you explain this?
In Grails domain classes don't extend a framework-provided base class, this is consistent with how persistent entities work in Hibernate.
Also be aware that static methods don't get inherited anyway, and no fields get inherited. The mapping and constraints variables are static fields declared on the domain class. Inheritance doesn't apply here.
Grails knows which classes are domain classes, services, controllers, etc. based on where their files are in the project structure. Grails knows to look for static variables in the domain objects named constraints and mapping.
This much is leveraging of conventions, not meta-programming. Meta-programming would be involved in implementing the DSL for the entries in those closures, and in adding GORM methods to the domain classes.

Where do a Grails controller's "expando" methods come from?

According to the documentation, a Grails controller is simply a class with "Controller" appended to the name and saved in grails-app/controllers/. The simplest of such a class being:
package some.package
class FooController {
def index = {}
}
When the Grails application is run, this controller will inherit some standard methods like getParams and getSession. From the attached screenshot I can see that these are added via groovy.lang.ExpandoMetaClass. What I don't see is how this happens. The controller doesn't implement any interfaces or extend any abstractions. Where do these methods come from?
From Grails 2.0 a new methodology was adapted to add the dynamic methods to the controller artefacts. You can visit them step wise to see how those properties are added to Controllers:-
Controller in grails is not part of grails-core but a plugin by itself to grails named grails-plugin-controllers.
Being a plugin, the corresponding *GrailsPlugin Class would define the behavior of the plugin.
ControllersApi (extending CommonWebApi) bears all those properties which are to added to controller artefact. (Introduced from Grails 2.0)
ControllerGrailsPlugin registers ControllersApi as a spring bean.
There is more to just adding ControllerApi as a bean.
There is a concept of MetaClassEnhancer which would take/consider an API (in this case ControllerApi) and enhance/reconcile the artefact (controller) with the corresponding API by adding CachedMethods to the artefact using reflection, which is the role of a BaseApiProvider present in grails-core.
This magic happens in the Controller plugin class as well.
Now, prior to Grails 2.0 a different method was adapted to add the dynamic properties to controller. That way metaClass properties were added to controllers at runtime which was found to be less efficient. Each of the dynamic property was represented by its own class (viz: GetParamsDynamicProperty, GetSessionDynamicProperty) which is not in use right now.
So what you need to look now in the object tree for those dynamic methods is this where the CachedMethods are available in the controller. Hope that helps. I would be glad to add more if you seek more. :-)
You are right, Grails 'Controllers' are not really Controllers in the sense they inherit from a base class, but rather they are just simple POGOs that follow the convention of being placed in the grails-app/controllers directory. When your application is compiled, 30+ methods are mixed in through AST transformations, the majority of them coming from
org.codehaus.groovy.grails.plugins.web.api.ControllersApi, but also from
org.codehaus.groovy.grails.plugins.converters.api.ConvertersControllersApi,
org.codehaus.groovy.grails.plugins.web.api.ControllersMimeTypesApi.
The preceding was paraphrased from Programming Grails by Burt Beckwith, and I would recommend it if you are interested in the finer details of Grails.
Quoting from Burt Beckwith's excellent book, Programming Grails:
Over 30 methods are mixed into controller classes with AST
transformations. The majority are added from the
org.codehaus.groovy.grails.plugins.web.api.ControllersApi class
ControllersApi source

Where should I place a transient domain class in a grails app?

Where should I place a transient domain class in a grails app?
Ie I have an Action class that will be passed about, and used, but never saved. Should this be in the grails-app/domain folder, or somewhere else?
grails-app/domain is for persistent domain classes, but not all of your application's domain-related classes need to be there, e.g. in this case where you want to use it as a value object. You can put these in src/groovy along with other classes that aren't considered Grails artifacts.
If you want the classes to support validation, you can annotate them with #Validateable - see section "7.5 Validation Non Domain and Command Object Classes" in the ref docs: http://grails.org/doc/latest/
I think a CommandObject may fit the bill. These typically go in the same directory as your controllers, have the same validation features available to domain objects, but are never persisted. Great for things like search forms.

Resources