Is there a way to make functions accessible across controllers in Grails? - grails

I want to be able to create global functions, meaning a function I can use across controllers, kind of like helper methods.
So in one controller I could do
useful_function(string) etc... Is this possible?
I did create a class in src/groovy called SiteHelper, am I on the right track? I want the methods of the class SiteHelper to be able to be used throughout controllers.

Yes, you're mostly on the right track. You may want to look at making it as a part of service layer.
http://grails.org/doc/latest/guide/services.html

You can add it to the metaclass of all controller classes, for example in BootStrap.groovy:
class BootStrap {
def grailsApplication
def init = { servletContext ->
for (cc in grailsApplication.controllerClasses) {
cc.clazz.metaClass.useful_function = { String s ->
return ...
}
}
}
}

The standard way to share logic between different components in Grails is to put it in a service, or alternatively in a taglib in the case of functions that need access to web-layer things like request/response/params/session/flash. You can call taglib tags as methods from any controller action:
MyTagLib.groovy
class MyTagLib {
def sayHello = { attrs, body ->
out << "Hello ${attrs.name}"
}
}
MyController.groovy
def someAction() {
def greeting = sayHello(name:"Ian")
// ...
}

I don't get what is nontrivial about this. It sounds exactly what Apache's StringUtils class or IOUtils class does. Yes, creating a SiteHelper with static methods and importing it will do what you want and is the typical practice for this in Java-influenced (and many other) languages.

Related

Reusable Grails controller helper methods

How to create reusable Grails controller helper methods which can be used in many controllers?
Right not I have few private methods in one controller. I want to share them with other controllers.
I would like have access to params, redirect etc.
The correct way to share code between controllers is to abstract the logic into a service. See
http://grails.org/doc/latest/guide/services.html
Note that if the service is not required to be transactional you should mark it as such.
If however you have web related logic (such as writing templates or markup to the output stream) then you can also use tag libraries to share logic, as tags can be invoked from controllers. See:
http://grails.org/doc/latest/guide/theWebLayer.html#tagsAsMethodCalls
You can use Mixins to you put all your common code:
// File: src/groovy/com/example/MyMixin.groovy
class MyMixin {
private render401Error() {
response.status = 401
def map = [:]
map.message = "Authentication failed"
render map as JSON
}
}
Now in a controller you can do something like this:
// File: grails-app/controller/com/example/OneController.groovy
#Mixin(MyMixin)
class OneController {
public someAction() {
if (!user.isAuthenticated) {
// Here we're using the method from the mixin
return render401Error()
}
}
}
Just one final advice: Mixins are applied during runtime so there is a little overhead.
The simplest answer is to create a class in src with a bunch of static methods and pass everything around as parameters, see: http://grails.org/doc/2.3.8/guide/single.html#conventionOverConfiguration
...or else create a controller base class that all other controllers extend from?
That said, I wonder if you are actually looking for scoped services? See http://ldaley.com/post/436635056/scoped-services-proxies-in-grails.

How to access the g.-namespace in a Domain Class

I would like to make use of the g.message() functionality in the toString method of my domain class, but the g.-namespace is not accessible by default.
I doubt that a import g.* will do the trick.
I already know that I can use the messageSource functionality, but it would be nicer to use the same syntax as in the views.
You can use:
class MyDomain {
def someMethod() {
def g = ApplicationHolder.application.mainContext.getBean( 'org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib' )
return g.message(....)
}
}
or else you can get messageSource directly: ApplicationHolder.application.mainContext.getBean('messageSource')
Using g.render in a grails service has some hints how to use "g:" in a service. I have not tested this, but it should work mostly the same in domain classes, with one important exception: a domain class cannot use InitializingBean since it's not a bean residing in the application context.

Grails: how to get a controller by controllerName in a filter?

I have a filter and the controllerName var getting controller target.
For example:
when user try to access /myApp/book/index, my filter is triggered and controllerName is equals book. How can I get a BookController instance?
Tks
EDIT:
I can get an Artefact using:
grailsApplication.getArtefactByLogicalPropertyName("Controller", "book")
But what I do with this artefact?
The controller will be registered as a spring bean. Just grab it by name:
applicationContext.getBean('mypackage.BookController') // or
def artefact = grailsApplication.getArtefactByLogicalPropertyName("Controller", "book")
applicationContext.getBean(artefact.clazz.name)
As Burt said, you probably don't want one controller instance inside your filter. This is a wrong way to solve your problem.
Grails Controllers as injected automagically by Spring Framework, and there is some black magic and procedures made when creating it. So, I can assure you this is not the way to solve this problem.
As you yourself described, you want to call your action, and I can imagine you're trying to reuse some code that resides in your action, maybe to generate some data in your database, or even to work with your HTTP session, am I right?
So, you can do two things to solve this kind of issue.
1) Just redirect your request flow to to your controller/action like this:
if (something) {
redirect controller: 'xpto', action: 'desired'
return false
}
2) Or you can get the logic inside your action (that is doing that dirty job you want to run), separate that logic inside one service, and reuse the service in both classes (action / service) this way:
MyService.groovy
class MyService {
def methodToReuse() {
(...)
}
}
MyController.groovy
class MyController {
def myService //auto-injected by the green elf
def myAction = {
myService.methodToReuse()
}
}
MyFilters.groovy
class MyFilters {
def myService //auto-injected by the red elf
(...)
myService.methodToReuse()
(...)
}
[]s,
You should be able to call newInstance on the artefact you've retrieved. newInstance works just like the constructor so you can provide any parameters you would to a normal constructor call.
So you can probably just do:
def bookController = grailsApplication.getArtefactByLogicalPropertyName("Controller", "book").newInstance()
Working code:
import org.codehaus.groovy.grails.web.context.ServletContextHolder
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.springframework.context.ApplicationContext
ApplicationContext applicationContext = (ApplicationContext) ServletContextHolder.getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
def grailsApplication
String nameController = "search"
def artefact = grailsApplication.getArtefactByLogicalPropertyName("Controller", nameController)
def controller = applicationContext.getBean(artefact.clazz.name)

Is it possible to create a controller that extends an abstract controller in grails?

Im wondering if something like this is possbile:
abstract class AbstractController {
def list = {
//default list action
}
}
class MyController extends AbstractController {
def show = {
//show action
}
}
Where AbstractController is not visible on the web i.e /app/abstract/list is not accessible and where MyController has the actions list and show and is accessible on the web as /app/my/....
Anyone ever done anything like this?
Try putting AbstractController into src/groovy folder.
Though, sharing functionality over Controllers might be not the best idea - it's better to move it to POGO classes or services. This question covers this issue partially: How do you share common methods in different grails controllers?
For recent version of grails (3.x as the time of writing) it would be better to use trait instead of extending an abstract Controller or use Mixin, the later was deprecated since introducing traits in groovy v2.3, here is an example of using a trait to add a generic behaviors to your controller:
1- Create your traits in src/groovy, e.g.
import grails.web.Action
trait GenericController {
#Action
def test(){
render "${params}"
}
}
2- Implement your trait as you implement any interface:
class PersonController implements GenericController {
/*def test(){
render 'override the default action...'
}*/
}
Note: traits can dynamically access all controller objects: params, response... and so on, and you still can override trait's action.
Hope this help.

Finding all controllers in application

How do i find all the controllers running in an application ?
I am trying to create a menu using YUI where only registered controllers will have a menu shown. A controller class will create a static list with various properties detailing name, action, etc. (much like grails-nav plugin).
I want to create a taglib that can find all controllers, identify which ones have this static list then look into each list and build up a menu.
I think i can use ControllerGrailsClass.metaClass.hasProperty to identify whether a given controller has the static property - but how do I find all the Controller classes to interrogate ?
Thanks in advance
You can get a list from the GrailsApplication object. Example:
class TestController {
def grailsApplication // gets injected automatically
def test = {
grailsApplication.controllerClasses.each { controllerArtefact ->
def controllerClass = controllerArtefact.getClazz()
println "$controllerArtefact, $controllerClass"
}
}
}
If you're not in a controller, you can get a hold of the grails application object like so:
import org.codehaus.groovy.grails.commons.ApplicationHolder
def grailsApplication = ApplicationHolder.application

Resources