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

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.

Related

How can I inject a custom service not associated with any domain class in grails?

I wish to make DAO layer in my grails project which would be not be associated with any of the domain classes and would be interacting with the secondary database of my project. I get the following error when I try to inject the service in any controller:
"Cannot invoke method abc() on null object"
However, the error is resolved and works perfectly when I initialise the service using the new keyword in the controller but I know that shouldn't be necessary as grails is supposed to handle it. Can anyone tell me what am I missing?
I don't think the issue has anything to do with whether or not the service is associated with a domain class. The DI container doesn't know anything about that.
If you have a controller like this:
// grails-app/controllers/demo/SomeController.groovy
package demo
class SomeController {
SomeService someService
def someControllerAction() {
someService.abc()
// ...
}
}
And a service like this...
// grails-app/services/demo/SomeService.groovy
package demo
class SomeService {
void abc() {
// ...
}
}
That will work fine.
It is almost impossible to say for sure without seeing what your code what you are doing wrong but one possibility is something like this, which will not work:
// grails-app/controllers/demo/SomeController.groovy
package demo
class SomeController {
def someControllerAction() {
// This is a local variable, not
// a property and as such will not
// be subjected to dependency injection.
SomeService someService
// ...
someService.abc()
// ...
}
}
Also, make sure the property name (someService in the sample above) matches the service class name, but with a lower case first letter (more generally, make sure the property name matches the property name representation of the service class name, which is usually as simple as lower casing the first letter of the class name).

Is there a way to make functions accessible across controllers in 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.

Grails: How do I use a request scoped service in a tag library?

I'm relatively new to the Grails community, but I love already what the engine has to offer. Currently, I'm implementing a custom tag library in order to easily facilitate a standard design on our pages. However, I need a way of calling helper functions for utility purposes (e.g. filtering data) and to stash request level meta data about my tags (e.g. counters, parent/child relationships).
I have attempted two solutions:
First: I've created a service, set its scope to "request"
package myapp
class CustomTagService {
static scope = 'request'
def data = []
def add(localData) {
data.add(localData)
}
}
However, when I try to inject it in my tag library
package myapp
class MyTagLib {
def customTagService
def myTag = { attrs, body ->
customTagService.add(attrs)
}
}
The engine yells at me for referencing a request scope (after a long painful stacktrace): "Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton"
Second: I understand the pageScope is available to me inside of a tag closure, and I've exploited it before. However, for the structure I am wanting, encapsulation would be much preferred.
Please let me know if I am going down the wrong path on this. Any suggestions would be much appreciated!
You can't use scoped beans in singleton beans since the singleton beans (including taglibs) are created at startup when there's no request active. Instead use a scoped proxy (a bit complicated) or just get the bean from the ApplicationContext for each use at runtime when there is an active request:
package myapp
class MyTagLib {
def grailsApplication
def myTag = { attrs, body ->
customTagService.add(attrs)
}
private getCustomTagService() {
grailsApplication.mainContext.customTagService
}
}

Grails and Groovy metaclass package name convention

I would like to be able to place my metaclasses in the package groovy.runtime.metaclass according to the convention defined here using the delegating meta class. I have placed MyClassMetaClass in groovy.runtime.metaclass.mypackage. for the concrete class mypackage.MyClass{}. Unfortunately calls to MyClass{} are not intercepted by MyClassMetaClass{}. Perhaps this is related to how grails initializes..?
It is possible to use a delegating meta class in a grails project by changing BootStrap.groovy.
import org.codehaus.groovy.runtime.InvokerHelper
class BootStrap {
def init = { servletContext ->
def myMetaClass = new MyClassMetaClass(MyClass.class)
InvokerHelper.metaRegistry.setMetaClass(MyClass.class, myMetaClass)
}
def destroy = {
}
}
Ofcourse this is not the same as implementing delegating meta classes as mentioned in the question.

Change the name of an injected service in grails

Can't seem to find info on this, if you have some, please point me to the right thread/post/link!
I have a service, lets say it's called 'SomeServiceWithAReallyLongNameICannotChange'. Of course the normal way to use services is to allow grails to inject them using either typless or typed notation:
class SomeClass{
//...
def someServiceWithAReallyLongNameICannotChange
//...
}
-- or --
class SomeClass{
//...
SomeServiceWithAReallyLongNameICannotChange someServiceWithAReallyLongNameICannotChange
//...
}
What I would like to do is rename the service to something shorter, but only where I'm using it, as I cannot change the name of the actual service. I tried using 'as' notation like you do with imports, and I tried changing the name in the typed declaration, but none of these things seem to work. Is this possible?
I tried something like this:
class SomeClass{
//...
def someServiceWithAReallyLongNameICannotChange as SS
//and I tried
def SomeServiceWithAReallyLongNameICannotChange SS
//no joy
//...
}
Thanks for your help!
The solution is to use autowire by type. By default grails uses autowire by name and hence you have to declare the service with the same name as the bean.
Here's example
class FooController {
boolean byName = false //set autowire by type
SomeReallyLongService service
}
You have to define a boolean variable byName and set it to false
You can not use def but must use the actual type of the service when declaring the service
It will enable autowire by type for whole controller, so all other dependencies will also be autowired by type
It is explained here
Update:
It is even possible to use Autowired annotation along with Qualifier.
Example:
class MyController {
#Autowired
#Qualifier("myServiceWithDifferntName")
def someService
}
You should be able to create a new bean via resources.groovy
beans = {
ss(SomeServiceWithAReallyLongNameICannotChange)
}
You can then inject it normally:
class SomeClass {
//...
def ss
//...
}
Another option, if you don't want to use autowire by type through the whole controller, nor do you want to add a custom bean for some reason, is to add a simple method to your controllers where you need the service, like so:
def someServiceWithAReallyLongNameICannotChange
def getSS() { someServiceWithAReallyLongNameICannotChange }
Then you could reference the service using ss.someMethod() anywhere within that controller.
However, this still requires you to add a chunk of code for every controller you use this service, or you will have inconsistent naming of the service.
(Personally, I think that doelleri's method is the best, since it allows you to be consistent in naming without modifying individual classes.)
I tried it and it's not working for me on Grails 3, did this by doing
import org.springframework.beans.factory.annotation.Autowired
...
#Autowired
PaymentStrategyService paymentStrategySvc

Resources