2011-11-25 12 views
11

Estoy buscando maneras de cómo limpiar mi código de controlador Grails. En varios controladores que tienen más o menos la misma lógica ..¿Cómo hacer que los controladores Grails sean más DRY?

  • obtener el objeto
  • cheque si existe
  • etc ..

¿Existe una manera sugerida en la fabricación de las acciones del controlador reutilizar el código común?

--- --- solución

Todas las respuestas a la pregunta han contribuido a la solución que hemos implementado.

Creamos una clase que se utiliza en nuestros controladores utilizando el enfoque Mixin. Uno de los métodos que expone la mixina es el método withObject. Este método toma el nombre de dominio del controlador y lo utiliza como una base para el método. ¡Este comportamiento puede ser anulado, por supuesto!

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" 
    } 
} 

¡Todas las respuestas han contribuido a la solución! ¡Muchas gracias!

+4

Por favor edite la pregunta y agregue un ejemplo de su código. Por el momento, la pregunta es muy vaga. – ordnungswidrig

Respuesta

8

siempre me tire de esta entrada del blog cuando este pregunta surge:

http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/

Básicamente, usted tiene un ayudante privado para varios dominios en los controladores.

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" 
    } 
} 

La manera de codificar el captador es muy flexible y se utiliza, para mí (que no esté prevista en el blog) es para la edición, etc.

I Código normalmente de esta manera (me gusta el modelo para su división clara y legibilidad):

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"]) 
    } 
} 

Con mi captador ayudante ser:

private def withIssue(Closure c) { 
    def issue = Issue.get(params.id) 
    if(issue) { 
     c.call issue 
    } 
    else { 
     response.sendError(404) 
    } 
} 

yo creo que el método mixin (muy similar a la forma de 'extender un controlador abstracto común') también es bueno, pero de esta manera ofrece dos ventajas:

  1. Usted puede escribir el ayudante, como ve que hago en el cierre que le da acceso a los métodos, etc. en STS/IDEA (Netbeans no probados)
  2. La repetición no es muy alta, y la capacidad de cambiar el captador (para usar para ejemplo BarDomain.findByFoo (params.id), etc)

en la vista ato editar() que sólo hay que poner un id="${issue.id}" en el <g:form> y funciona sin problemas.

+0

Gracias por sus comentarios, combiné más o menos algunas respuestas en este hilo con una solución de trabajo a mi lado. Utilizo la estrategia mixin junto con un método genérico conObject. Actualizaré mi pregunta con el código que estoy usando ahora. – Marco

0

Implemente el controlador abstracto con métodos comunes (use la directiva 'protegida') y extienda desde allí sus controladores reales. No use palabras 'get' y 'set' al comienzo de los nombres de este método. No es bueno, pero funciona.

+1

¿Por qué sugerir no utilizar palabras 'get' y 'set' al comienzo de los nombres de este método? – gotomanners

+0

para evitar identificarlos como 'getter' o 'setter'. – jenk

6

No recomendaría la herencia para eso, ya que no puede extender los métodos genéricos en varias súper clases. Su clase abstracta se volvería desordenada rápidamente si tiene muchos controladores. No puede usar la composición (por ejemplo, utilizando un servicio) porque no tiene acceso a response, render, o params directamente desde allí.

El enfoque que uso es inyectar métodos genéricos a través de Mixins.

@Mixin(ControllerGenericActions) 
@Mixin(ControllerUtil) 
class BookController { 
    def show = &genericShow.curry(Book) 

    def exists = { 
    render(idExists(Book)) 
    } 
} 

La primera acción show utiliza un método genérico en ControllerGenericActions.groovy, con un argumento binded a ella. El segundo uso de un método mixin idExists está dentro de una acción de controlador.

Aquí hay un código de ejemplo para src/groovy/ControllerGenericActions.groovy

class ControllerGeneric { 
    def genericShow(Class clazz) { 
    render clazz.get(params.id) as XML 
    } 
} 

y en src/groovy/ControllerUtil.groovy

class ControllerUtil { 
    def idExists (Class clazz) { 
    return clazz.get(params.id) != null 
    } 

No muy útil en este caso, pero se entiende la idea.

+0

No estoy realmente en el uso de cierres, así que por favor perdone si estoy cometiendo un error. Pero si se crea un método como 'genericShow (Class clazz)', ¿puedes curry ese método? Siempre tuve la impresión de que un método no puede cursarse, pero puede hacerlo un cierre. – Marco

+0

Esa es la razón para usar & antes del nombre del método: convierte un método en un cierre. – Antoine

+0

Tengo una pregunta sobre esta solución: ¿Se puede probar la unidad de mezcla ControllerGeneric? Lo pregunto porque 'render' es un método proporcionado por Grails mientras mejora los controladores. –

Cuestiones relacionadas