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.
Related
I am in the phase of building a rendering framework for rendering my models in different formats.
My idea is the following:
public class ResidenceRendererShort : IRender<Residence> {
public string Format() {
return "short";
}
public string Render(Residence content) {
return content.Name; // Could return a whole lot of HTML
}
}
I can have multiple of those with different formats, and they are all injected using Ninject DI into my RenderingService, where I got methods for finding the correct render, using methods like e.g. FindRendererFor(Type type, string format)
Now my question is, how can I create a tag in razor which will use the rendering service and applying the correct render? I have been looking into HtmlHelpers, but they are static methods and I can not inject my RenderingService into this.
I thought I could create something like:
#Model my.namespace.Residence
#Html.RenderObject(Model, "short");
Am I missing something or someone got an idea on how to accomplish this?
You're killing yourself. Just use Display/Editor Templates. If you have a view in ~/Views/Shared/DisplayTemplates or ~/Views/Shared/EditorTemplates named after your class, Residence.cshtml in this case, then Razor will use this view to render your class whenever it's passed to Html.DisplayFor or Html.EditorFor.
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.
When you generate controllers in grails, the controllers call methods on the domain layer directly - I quite don't understand this, every bit of me is telling me that this is kind of wrong because you are tightly coupling the backend with the frontend. I think this belongs to the service layer.
Since it would be pretty ugly to create an equivalent set of methods in the service layer for all the methods defined on domain objects, I created this AbstractService to delegate all (missing) method calls from the service layer to the domain layer:
abstract class AbstractService {
def entityType
/**
* By default, this method takes the name of the service that extends this
* class, removes the suffix 'Service' and tries to create the Class object
* from the resulting name. Override at will.
*/
protected Class getEntityType() {
if (!entityType) {
try {
entityType = Class.forName(this.class.name[0..-8], false, Thread.currentThread().contextClassLoader)
} catch (ClassNotFoundException e) {
throw new ClassNotFoundException("Class ${this.class.name[0..-8]} could not be found. Please "
+ "override AbstractService#getEntityType() for ${this.class}.")
}
}
entityType
}
def methodMissing(String name, args) {
try {
if (getEntityType()?.metaClass?.getStaticMetaMethod(name)) {
getEntityType().invokeMethod(name, args)
} else if (args?.last()?.metaClass?.getMetaMethod(name)) {
args.last().invokeMethod(name, args.take(args.size() - 1))
} else {
throw new MissingMethodException(name, this.class, args)
}
} catch (MissingMethodException e) {
throw new MissingMethodException(name, this.class, args)
}
}
}
Then I just extend this service e.g. like this:
class UserService extends AbstractService {
}
And my controllers then can look for example like this:
class UserController {
def userService
def create() {
userService.save(new User(params))
}
def list() {
userService.list(params)
}
// et cetera...
}
Don't you think this is better? Thanks to dependency injection, I can for example rewrite the whole business layer without the need to change the code in the controllers - which is kind of why we use dependency injection, isn't it?
Thanks for your answers, I would like to hear as much opinions as possible.
This model is very used in Java Web applications and all. The Rails (and Grails followed it) community just tried to break the paradigm here, leaving it more simple. I mean, why would you delegate a service class to manipulate an entity, if this entity can simply do the job? If it's natural to the entity to do the job, then don't bring someone else to do it. That way, you avoid the Anemic Model since your objects are not only data holders, but they also know how to operate its own business.
Having said that, there are times when you're better off using a service class to do operations on your entities. For example, if it involves different kind of entities at the same time and so on... So, when it's not "natural" (and you would have to force to make it work) for the entity itself to take care of the operation, then a service class is the way to go. This article based on Rails gives some tips about the use of a service class.
And you are not tightly coupling the controller with the models (you said backend and front end, but I guess that's what you mean). The controller will eventually need to use the model, be it the entity itself or a service class (also Model) manipulating it.
The scaffolded controller code does not really represent ideal application architecture. Keep in mind that the generated scaffold code is just a starting point for generating the CRUD portions of your application.
You are correct that in general, you don't want to put most of your GORM queries in Controllers, since controllers are supposed to be for interacting with the front end. You can certainly either put the query/business logic into Services or put the queries directly into Domain classes. That's why Grails Services support declarative transaction handling.
I'm rewriting the views for my site but I'd still like to have the originals running because the rewrite is missing features. Is there a way for me to have both and depending upon the URL pick one or the other? They'll be sharing the same domain and controller classes.
For example, http://localhost/app/* goes to the original views and http://localhost/app/test/* goes to the new views.
Thanks!
The context path when using the render function uses the name of the controller by default.
What you could do is add a parameter in your url mapping, is this satisfying ? :
class UrlMappings {
static mappings = {
"/test/$controller/$action?" {
indent = "newviewfolder/"
}
}
}
And, in a controller method
class RandomController {
render(${params.indent}"viewname", model [:], params)
}
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.