The project I'm working on currently uses filters for various pieces. The filters themselves work great. The problem I'm running into is that when specifying which controllers the filter should be executed on, I end up with a very large list. Functionally this works fine but it ends up being ugly and somewhat unwieldy.
def filters =
{
filterSomething(controller:'one|two|three|...|xyz', action:'*')
{
//before filter here, not important.
}
}
Is there a way to specify that the filter is only applicable for controllers in a given package or list of packages?
If there is nothing out of the box, I was thinking about tying something into the bootstrap and setting my lists that way.
You could have a filter on all controllers and then check their package and then decide what you want to do. There are better and more elegant way coding this, but just to give you an idea.
class MyFilters {
def grailsApplication
def filters = {
all(controller:'*', action:'*') {
before = {
if (checkController(['com.package.name'],controllerName)){
}
}
}
}
def searchInList(list,packageName){
for (keyword in list) {
if (packageName.contains(keyword)) return true
}
return false
}
Boolean checkController(def includePackageList,cname) {
def dlist = grailsApplication.getArtefacts("Controller")
def filteredList= dlist.findAll{ searchInList(includePackageList,it.getPackageName()) }
return filteredList.contains(cname)
}
}
Related
I've got a pretty clean filter which is intended to add an attribute to the model Map so that it can be shown on every page:
def filters = {
someFilter(controller:'*', action:'*') {
after = { Map model ->
model.something = 'hey!' // can't, since it's null
}
}
}
If model is null it fails horribly (NullPointerException, as expected). But if I add an if (!model) and try to instantiate it, it's local and doesn't behave as intended.
How can I put an empty map there when model is null?
I think you can do it with a little groovy triks. You can create a groovy interceptor and apply it to all controllers class in bootstrap. The interceptor implements the afterInvoke method in which check for null results and turn them in empty maps [:]
Try this:
def filters = {
someFilter(controller:'*', action:'*') {
after = { Map model ->
model.put('something', 'hey!')
}
}
}
In grails controller examples, I have seen save(Model modelInstance) and save(). I tried them both, both of them works. I imagine grails instantiates the modelInstance with the params. Is my assumption correct?
I also noticed in index(Integer max), does the param has to be named max? or any name would work as long as it is a number?
How does these passing of arguments work underneath?
If you write a controller like this...
class MyController {
def actionOne() {
// your code here
}
def actionTwo(int max) {
// your code here
}
def actionThree(SomeCommandObject co) {
// your code here
}
}
The Grails compiler will turn that in to something like this (not exactly this, but this describes effectively what is happening in a way that I think addresses your question)...
class MyController {
def actionOne() {
// Grails adds some code here to
// do some stuff that the framework needs
// your code here
}
// Grails generates this method...
def actionTwo() {
// the parameter doesn't have to be called
// "max", it could be anything.
int max = params.int('max')
actionTwo(max)
}
def actionTwo(int max) {
// Grails adds some code here to
// do some stuff that the framework needs
// your code here
}
// Grails generates this method...
def actionThree() {
def co = new SomeCommandObject()
bindData co, params
co.validate()
actionThree(co)
}
def actionThree(SomeCommandObject co) {
// Grails adds some code here to
// do some stuff that the framework needs
// your code here
}
}
There is other stuff going on to do things like impose allowedMethods checks, impose error handling, etc.
I hope that helps.
I have a fairly large criteria closure in my Grails application, and I would like to reuse part of it in several places in my application. Rather than duplicating the section I need to reuse, I'd like to define this as a separate closure and reference it wherever it is needed, but I am struggling a bit with the syntax.
This is a simplified / cut down version, but essentially my criteria looks something like this:
def criteriaClosure = {
and {
// filtering criteria that I'd like to reuse in lots of places
or {
names.each { name ->
sqlRestriction(getFilteringSql(name), [someId])
}
}
if (isOrganisationChild(childDefaultGrailsDomainClass)) {
sqlRestriction(getFilteringSql(domain), [someArg])
}
// filtering criteria that's specific to this particular method
sqlRestriction(getSomeOtherSql(), [someOtherArg])
}
}
def criteria = domain.createCriteria()
def paginatedList = criteria.list([offset: offset, max: max], criteriaClosure)
I've tried defining the part of the closure I want to reuse as a variable, and referencing it in my criteria closure, however the restrictions it defines don't seem to be applied.
def reusableClosure = {
and {
or {
names.each { name ->
sqlRestriction(getFilteringSql(name), [someId])
}
}
if (isOrganisationChild(childDefaultGrailsDomainClass)) {
sqlRestriction(getFilteringSql(domain), [someArg])
}
}
}
def criteriaClosure = {
and {
reusableClosure() //this doesn't seem to work
sqlRestriction(getSomeOtherSql(), [someOtherArg])
}
}
I'm sure this must be a pretty straightforward thing to do, so apologies if it's a daft question. Any ideas?
I think you have to pass the delegate down to the reusableClosure, ie:
def criteriaClosure = {
and {
reusableClosure.delegate = delegate
reusableClosure()
sqlRestriction(getSomeOtherSql(), [someOtherArg])
}
}
From what is in the Grails user guide, the recommended way to send different content formats based on the content negotiation is to use a withFormat block:
import grails.converters.XML
class BookController {
def list() {
def books = Book.list()
withFormat {
html bookList: books
js { render "alert('hello')" }
xml { render books as XML }
}
}
}
However, I would like the responses of ALL my controller methods to do this. Is there a better way to get this behavior than to simply copy-paste the withFormat block at the end of every content-returning action?
First two things that popped in my head were Interceptors and Filters:
http://grails.org/doc/1.3.7/ref/Controllers/afterInterceptor.html
http://grails.org/doc/latest/guide/6.%20The%20Web%20Layer.html#6.6 Filters
Interceptors won't work because you can't do the withFormat. Bummer because that's a more global.
Filters will work on a controller per controller basis, but at least you'd be minimizing your duplication at that level.
def afterInterceptor = {model, modelAndView ->
withFormat {
html { model }
js { render "alert('hello')" }
xml { render model as XML }
}
}
This worked for me in my test project. I tried putting that closure into it's own class and mixing in the class so that you could do a more global solution... no dice though.
Maybe have all of your afterInterseptors pass the model, modelAndView to a common class? That seems to work :) (working towards an answer while answering)
#Mixin(AfterInterceptorWithFormat)
class FirstController {
def action1 = {}
def action2 = {}
def afterInterceptor = {model, modelAndView ->
performAfterInterceptor(model, modelAndView)
}
}
class AfterInterceptorWithFormat {
def performAfterInterceptor(model, modelAndView) {
withFormat {
html { model }
js { render "alert('hello')" }
xml { render model as XML }
}
}
}
Give that a whirl and let me know what you think.
What I ended up doing is to simply adjust the default CRUD templates to have the withFormat block at the end of each method.
I realized that what I really wanted was just content-negotiated CRUD, so putting the code into the templates was all I needed for that.
The rest of the controllers for the non-crud parts of my application did not need no-html output, so I didn't need content negotiation on anything but the crud controllers.
I am looking for ways on how to cleanup my Grails controller code. In various controllers i more or less have the same logic..
get the object
check if it exists
etc..
Is there a suggested way on making controller actions reuse common code?
--- solution ---
All answers to the question have contributed to the solution we have implemented.
We created a class that is used in our controllers using the Mixin approach. One of the methods that the mixin exposes is the withObject method. This method takes the domainname from the controller and uses this a base for the method. This behaviour can be overridden of course!
def withObject(object=this.getClass().getName()-"Controller", id="id", Closure c) {
assert object
def obj = grailsApplication.classLoader.loadClass(object).get(params[id])
if(obj) {
c.call obj
} else {
flash.message = "The object was not found"
redirect action: "list"
}
}
So all answers have contributed to the solution! Thanks a lot!
I always pull out this blog post when this question comes up:
http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/
Basically you have a private helper for various domains in your controllers.
private def withPerson(id="id", Closure c) {
def person = Person.get(params[id])
if(person) {
c.call person
} else {
flash.message = "The person was not found."
redirect action:"list"
}
}
The way you code the getter is very flexible and a typical use for me (that is not covered in the blog) is for editing etc.
I normally code this way (i like the pattern for its clear division and readability):
def editIssue() {
withIssue { Issue issue ->
def issueTypes = IssueTypeEnum.values().collect {it.text }
[issueTypes:issueTypes,activePage:"issue", issue: issue]
}
}
def doEditIssue(IssueCommand cmd) {
if(cmd.validate()) {
withIssue { Issue issue ->
issue.updateIssue(cmd)
redirect(action: "show", id: issue.id)
}
}
else {
def issueTypes = IssueTypeEnum.values().collect {it.text }
render(view: "edit", model:[issueTypes:issueTypes,issue:cmd,activePage:"issue"])
}
}
With my getter helper being:
private def withIssue( Closure c) {
def issue = Issue.get(params.id)
if(issue) {
c.call issue
}
else {
response.sendError(404)
}
}
I do think that the mixin method (very similar to the 'extend a common abstract controller' way) is nice too, but this way gives two advantages:
You can type the helper, like you see I do in the closure giving you access to the methods etc in STS/IDEA (not tested Netbeans)
The repetition is not very high, and the ability to change the getter (to use for example BarDomain.findByFoo(params.id) etc)
In the view I bind to edit() I just put an id="${issue.id}" in the <g:form> and it works seamlessly.
I wouldn't recommend inheritance for that, as you can't spread generic methods in several super classes. Your abstract class would quickly become messy if you have many controllers. You can't use composition (for instance using a Service) because you don't have access to response, render, or params directly from there.
The approach I use is to inject generic methods via Mixins.
#Mixin(ControllerGenericActions)
#Mixin(ControllerUtil)
class BookController {
def show = &genericShow.curry(Book)
def exists = {
render(idExists(Book))
}
}
The first action show uses a generic method in ControllerGenericActions.groovy, with an argument binded to it. The second use of a mixin idExists method is inside a controller action.
Here is an example code for src/groovy/ControllerGenericActions.groovy
class ControllerGeneric {
def genericShow(Class clazz) {
render clazz.get(params.id) as XML
}
}
and in src/groovy/ControllerUtil.groovy
class ControllerUtil {
def idExists (Class clazz) {
return clazz.get(params.id) != null
}
Not very useful in this case, but you get the idea.
Implement abstract controller with common methods (use 'protected' directive) and extend from it your real controllers. Do not use 'get' and 'set' words at the beginning of this method's names. Not good, but it works.