Groovy metaprogramming - grails

In a Service of a Grails project, I like to find, at run time, the arguments of Dynamic Methods in order to inform callers.
Also, I like to call the method and if doesn't exist to return an error,
I will appeciate any help.

You can configure URLMappings in grails to get the value of the dynamic method and call it against your object for example you can do the following
In your urlMappings.groovy define a mapping with two embedded variables object and method
"/$object/$method" (controller:"api",action:"invoke")
Define a 'api' controller with an invoke action. See code below with the logic on how to invoke the method on the object
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
class ApiController {
def invoke = {
def object = params.object
def method = params.method
def args
if(object) {
def domainClass = AH.application.domainClasses.find{it.name == method}?.clazz
if(domainClass.metaClass.getStaticMetaMethod(method,args)) {
domainClass.metaClass.invokeStaticMethod(target,input.method,args)
}
}
}
}
In my example, I assumed that you're calling a static dynamic finder on the domain class. You can generalize this to handle instance methods as well. You need however to provide more information such as the object id, in your request to load the object and call the method against it.
"/$object/$id/$method" (controller:"api",action:"invoke")
-Ken

Not sure I understand your question, but the last part about checking if you can call a method on an object, this can be done by checking the meta class of the object you are dealing with like this.
obj.metaClass.respondsTo(obj, 'theMethodYouWantToCall')
obj is the object you want to call the method on, and theMethodYouWantToCall is the name of the method you want to call.
respondsTo will return an empty list [] if the method you are trying to call is not found

Related

Grails Controller integration test executes actions of the parent, RestfulController class

I have a weird problem with integration testing restful controllers... In the following code snippet, when I make a post request from tests, the save method of the parent, RestfulController class is called instead of the save method of the child class, MyController and because they have different signatures, this ends up resulting in a UNPROCESSIBLE_ENTITY response.
class MyController extends RestfulController<MyDomain> {
static responseFormats = ['json', 'xml', 'hal']
MyController() {
super(MyDomain)
}
def save(MyCommand command) {
...
}
}
When I run the following test, the save() action of my controller's parent class, RestfulController gets executed, thus leading to UNPROCESSIBLE_ENTITY response, since I am using a Command object which is different from my domain class.
void "Test the save action correctly persists an instance"() {
when: "The save action is executed with valid data"
response = restBuilder.post(resourcePath) {
accept('application/json')
header('Authorization', "Bearer ${accessToken}")
json validJson
}
then: "The response is correct"
response.status == CREATED.value()
response.json.id
Vote.count() == 1
}
What can I do to fix this, please?
Overloading controller actions is not supported. You can override them, but you can't overload them.
What is happening is the framework is invoking the no-arg save() action in the parent class, which never invokes your method (nor should it).
You can rename your save(MyCommand command) so it doesn't have the same name as an action in your parent class and then provide a corresponding URL mapping and you will be on your way. Depending on what you want to do in the action, that may or may not be the best thing, but that is 1 path you can take.
I hope that makes sense.

Grails - Pass data from view to controller

I'm trying to pass Param data from view to controller and I'm having trouble. Here's what I'm currently trying to do.
View:
<form action="${doStuffURL}" method='post' params="[keyId: ${mykey.id[i]}]"><g:actionSubmit value="doStuff"/></form>
Controller:
def myObjectService //inject service object
def doStuff = {
myObjectService.doStuff("${params.keyId}") //this blows up because it's value of NULL
myObjectService.doStuff(8) //this works fine
}
It gets to the method block because the error log says "no signature of method MyObject.doStuff() is applicable for argument types: values: [null]." Also, I'm able to see ${mykey.id[i]} data from another field, so the data is definitely in the view.
How can I get the controller to read the Param data?
Thanks!
err lots wrong here:
<form action="${doStuffURL}" method='post' params="[keyId: ${mykey.id[i]}]"><g:actionSubmit value="doStuff"/></form>
why not use:
<g:form name="something" controller="yourController" action="youraction">
As you can see above you are having to generate
form url (maybe you have your reasons)
Controller:
def doStuff = {
MyObject.doStuff("${params.keyId}")
}
Differences between action and methods in Grails controllers
So firstly why you should change controller but my actual concern here is
MyObject.doStuff
is MyObject.doStuff a static method ?
since that is only when a call like this would work. The link shows a static method. gets called here and it may confuse you due to it calling it via executor.registerSenderFault due to how it is generated working - expandability - for future classes that do same thing. this could have been EmailExecutor.registerSenderFault which is the full class in uppercase like you have declared.
surely it should be a service notice starting with lower case.
myObjectServive.doStuff(stuff)
If above is some real method in MyObject and is not a static method then you need to instantiate the class
MyObject myObject = new MyObject()
myObject.doStuff(stuff)
but in short this is why services exist it is all there to save you all the hassle since they just get injected.
I suggest you do some reading looking around
E2A
def doStuff = {
println "my params are $params "
//do you see keyId in there ?
//if so what is doStuff expecting as input a string ?:
// since if you do
println "${params.keyId.getClass()}"
//by default it will be a string you may need to change it from:
//myObjectService.doStuff("${params.keyId}")
myObjectService.doStuff(params.keyId as Long)
}
Personally I don't think it is any of the above edited comments, it still relates to how/what you are injecting. I have seen similar issues. I would suggest you create a brand new service and inject new service as a test and start again - not convinced you were injecting it correctly or if you are the service may be some abstract class rather than a normal service. Or.... you are making some form of similar mistake in the uppercase/lowercase declaration of the service name so you may have created:
MyObjectnameService and calling it using myObjectNameService difference in N in those or.... even worse you have created actual service as myObjectNameService with lowercase name.
test this all again using a much simpler naming convention and create a new service as a test
grails create service MyService
and try again using this service

how to mock a class inside a controller?

I have a controller method which inside it creates a domain instance and saves it. I need to simulate that this save fails so i am thinking of mocking this domain and for save method i throw an exception. Is there a way to mock the donor class.
def method(){
...
Donor donor = new Donor()
donor.properties = convertedParams
donor.save(failOnError: true)
...
}
I know how to mock a service that is used inside a controller
def mock = new groovy.mock.interceptor.MockFor(ExampleService)
def exampleService = controller.exampleService
mock.demand.exampleMethod(){ Long id, TransactionResponse tr ->
throw new RegistrationActivationException(new IllegalStateException('something went wrong'), [])
}
mock.use{
controller.exampleService = new ExampleService()
model = controller.exampleMethod()
}
So, the code above will mock out the ExampleService service used in the controller. Is there a way to do mocks like that but not for a service but for a domain that is instantiated inside a method like in the first code block above. I need to mock out the Donor class and when save is called then i need to throw an exception in order to simulate and test what happens when the donor save fails.
I appreciate any help! Thanks!
I need to mock out the Donor class and when save is called then i need to throw an exception in order to simulate and test what happens when the donor save fails.
This seems like a very convoluted way to achieve an exception being thrown on save. Instead, it seems you could just try to save an instance of Donor that doesn't match it's constraints. Assuming Donor has at least one property that is not null, this should achieve the same thing
new Donor().save(failOnError: true)

In Grails can we access methods without creation of objects?

as I am new to grails but I am familiar with Java.In the following code I have Artist domain class and ArtistController class. In controller class the Artist accessing the findByName(...) directly I mean its not creating the object to access the method (or) is findByName(..) a static method so that it can be accessed using the className.staticMethodName as in Java.
class ArtistController {
def show() {
def artist = Artist.findByName(params.artistName)
// do whatever is appropriate with the artist...
}
}
You are referring to static methods. It has nothing to do with Grails vs Java, as static is present in Java also. You may want to check out the docs, but in a nutshell, a static method belongs to the class, and not an instance of that class, which is why you are able to invoke the method without a new object.

pass data from controller to filter

In a Grails application I'm looking for some way to pass data from a controller action to a filter that runs after the action. I was thinking of something like:
class MyController {
def myAction = {
render(view:"myView", model:[key: "value"])
passData {
// Do some processing here
name = "paolo"
age = 38
}
}
}
public class MyFilters {
def name
def age
def filters = {
myFilter(controller: "*", action: "*") {
after = { model ->
// Do something with name and age
return true
}
}
}
}
The original plan was to do the following in the init() closure of BootStrap.config:
Use meta-programming to add a "passData(Closure pdClosure)" method to all the controllers
Set the delegate of pdClosure to MyFilters, so that when the name and age properties are set within this closure, they are set on the MyFilters instance.
However, I realised this won't work as there's no obvious way for me to access (from BootStrap.init) the MyFilters instance that will be called for a particular controller.
Equally, there doesn't appear to be any way to access the controller instance from within the filter. Of course, I could just stuff all the data into the model, but I'm wondering if there's a more elegant way to pass data between the two?
Thanks,
Don
Passing your data in the model seems pretty darn elegant and easy to me. You can even remove the data from the model in the filter if you don't want it to be available to the view for some reason.
If in your filter you want to modify the data that is a model, then your solution looks fine.
If however you just want to pass some parameters from controller to the filter then request attributes should be better (if you need that data only the for request scope). This way you don't mix the model with some request parameters.

Resources