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.
Related
So I'm learning RoR and I have to 3 services that calls an API with the same structure and i want to know if i can do it with a parent class and then work with the parent class to save code.
Thanks!
Yes. This may work if you can define a method with fewer arguments, which builds that structure for the API call.
Approaches are:
Put that common method in a base class which the other classes inherit from.
Put that common method in a module as a mix in.
Write a class to handle the call to the API, which builds the structure.
I don't think you have an "isa" relationship from the sound of it. So unless you do, 2 is preferred to 1. You can only inherit from one class, so mixins are more flexible.
Approach 3 is a good idea. You can have internal methods for the hostname and other constants for your API call.
This can be combined with the other approaches as you can use the Aggregation pattern to aggregate the API object in the other classes. That might or might not make sense. It might be just as well as the other classes have methods which instantiate the class in approach 3 and call it.
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.
Do you suggest that I use Inheritance, Mixins, ExpandoMetaClass or something else?
If your common methods are largely related to one another, use inheritance from a common class containing them.
If however you have separate concerns and would benefit from grouping them into more than one file, I suggest traits - they have deprecated Groovy's #Mixin annotation.
I'm using them to decorate my controllers and they have worked out very nicely. I have read somewhere that the Grails team is also going to use them to replace the 'magic' metaclass decoration of artefacts like controllers (which is how methods like render are currently provided).. can't find the link, though :(
If you want to add this behavior to services in your application and also those from plugins, use the getServiceClasses() dynamic method in GrailsApplication and add to their metaclasses, e.g.
def grailsApplication
....
grailsApplication.serviceClasses.each { sc ->
sc.clazz.metaClass.foo = { bar -> ... }
}
The Grails FAQ says this:
Q: How can I access domain classes from sources in src/groovy?
Sometimes, you are developing some utility classes that live in src/groovy and which you intend >to use from Services and other artifacts. However, as those classes are pre-compiled by Grails, >it is not possible to instantiate them and write things like Book.findByTitle("Groovy in >Action"). But fortunately, there is a workaround since it's possible to do this:
import org.codehaus.groovy.grails.commons.ApplicationHolder
//…
def book = ApplicationHolder.application.getClassForName("library.Book").findByTitle("Groovy in Action")
The application MUST have finished bootstrapping before the dynamic Gorm methods will function correctly.
However, it appears that I can directly import domain objects and use GORM methods in my src/groovy classes without any problem, e.g.:
Book.findByTitle("Groovy in Action")
Since ApplicationHolder is deprecated, this advice must be out of date, but is there still any reason to avoid using domain classes directly from src/groovy?
You are correct, you referring to an out dated information. You can use domain classes inside classes defined under src/groovy.
The only overhead is that you have to handle transactions manually. On the contrary, services inside grails-app/services handes transaction by default. Services take care of transactions when the transactional flag is set to true (default is true of nothing specified).
On the other hand, when you access domain classes from src/groovy you have to use withTransaction block to handle transactions manually..
Book.withTransaction{status->
def book = Book.findByTitle("Groovy in Action")
book.title = "Grails in Action"
book.save()
status.setRollbackOnly() //Rolls back the transaction
}
Refer withTransaction for details.
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.