In a Grails application I would like to add a foo() method to all my controller classes. I know that I can do this inside a plugin's doWithDynamicMethods closure using code like:
application.controllerClasses.toList()*.metaClass*.foo = { println 'foo called' }
However, I don't want to create a plugin just for this purpose. Is there anywhere else I can do this. I suspect it might be possible within the init closure of BootStrap.groovy, but I don't know how to get access to the GrailsApplication instance in this closure.
Thanks,
Don
def grailsApplication = org.codehaus.groovy.grails.commons.ApplicationHolder.application
The question was asked a long time ago, so my answer might not have been possible back then - but now it's April 2014. This code shows the most straightforward way of adding a method to all your controllers (from within BootStrap.init):
grailsApplication.controllerClasses.each { controllerClass ->
if (controllerClass.clazz.name.contains("org.idurkan.foo")) {
controllerClass.metaClass.respondError = { String message, int status = 404 ->
response.status = status
render([errorMessage: message] as JSON)
}
}
}
In BootStrap.groovy, inject grailsApplication like this: def grailsApplication
Make a call like mine above, substituting your own package name - to avoid messing around with plugins' classes.
Now in any controller you can use the repondError closure (invoke it like a method).
Note this does not add an action! It's just a utility closure/method available in every
controller.
class BootStrap {
def grailsApplication
def init = { servletContext ->
...
}
}
Related
I have a global shared library on Jenkins implicitly loaded on all pipelines, then my Jenkinsfile is like that:
new com.company.Pipeline()()
And then the shared library has on directory src/com/company some files, below the Pipeline.groovy class:
package com.company
import static Utils.*
def call() {
// some stuff here...
}
The problem is, this way I have to static declare all methods, thus I lose the context and cannot access jenkins' methods easly without the Pipeline class' instance. As you can see here they passing this to the method mvn.
Thinking of avoid this I was wondering about dynamically add all methods as closures by calling Utils.install this instead of using import static Utils.*, then my Utils.groovy is something like that:
package com.company
private Utils() {}
static def install(def instance) {
def utils = new Utils()
// Some extra check needed here I know, but it is not the problem now
for (def method in (utils.metaClass.methods*.name as Set) - (instance.metaClass.methods*.name as Set)) {
def closure = utils.&"$method"
closure.delegate = instance
instance.metaClass."$method" = closure
}
}
def someMethod() {
// here I want to use sh(), tool(), and other stuff freely.
}
But it raises an GStringImpl cannot be cast to String error, I believe .& do not work with variables, how can I convert a method into closure having the method name on a variable? I have the MetaMethod mostly being a CachedMethod instance, if it were possible to turn it a ClosureMetaMethod instance maybe the problem can be solved, but whenever I search for method to closure conversion for groovy I just found the .& solution!
If I use instance.metaClass.someMethod = utils.&someMethod it do work, but I want it to be dinamic as I add new methods without needing to worry about sharing it.
There is a way to do it dynamically. Notation utils.&someMethod returns a MethodClosure object that can be simply instantiated with its constructor:
MethodClosure(Object owner, String method)
Consider following example:
class Utils {
def foo() {
println "Hello, Foo!"
}
def bar() {
println "Hello, Bar!"
}
}
class Consumer {
}
def instance = new Consumer()
def utils = new Utils()
(utils.metaClass.methods*.name - instance.metaClass.methods*.name).each { method ->
def closure = new MethodClosure(utils, method)
closure.delegate = instance
instance.metaClass."$method" = closure
}
instance.foo() // Prints "Hello, Foo!"
instance.bar() // Prints "Hello, Bar!"
In this example I use def closure = new MethodClosure(utils, method) to get object method reference and then add this method to instance object. I hope it helps.
I have an integration test where I sometimes want to mock the return of a service method. However, I have seen that once I mock that method, the subsequent tests that call it will also use the mocked function.
Is this normal? If so, how can I have test which sometimes use mocked functions and sometimes use the real implementation?
Here is my code:
MyController {
def someService
def save(){
...
def val = someService.methodToMock()//sometimes want to mock other times, not
...
}
}
MyTest {
def "test 1"(){
...
//I want to mock here
myController.someService.metaClass.methodToMock = { [] }
...
myController.save()
}
def "test 2"(){
...
//I don't want to mock here, however
// it is returning the mocked results
myController.save()
}
}
In general you don't want to change anything to do with metaclasses in integration or functional tests, only in unit tests. It's expected that you'll be doing this in unit tests and there's automatic support for restoring the original metaclass after each test or after each test class runs depending on the version of Grails and how things are configured. But this isn't the case in integration tests.
There are several different approaches you can use. If you use untyped dependency injection, e.g. def someService, then you can overwrite the real service instance with anything you want, and as long as it has the method(s) that you'll be invoking during the test method the controller won't know or care that it's not the real service.
I like to use a map of closures in this case, since Groovy will invoke a closure as if it were a method. So for 'test 1' you could do this:
def "test 1"() {
...
def mockedService = [methodToMock: { args -> return ... }]
myController.someService = mockedService
...
myController.save()
}
This works because you get a new instance of the controller for each test, and you change the service just for that instance, but the real service isn't affected at all.
Your controller invokes someService.methodToMock(), which is actually someService.get('methodToMock').call(), but the map access and closure invocation syntax can take advantage of Groovy's syntactic sugar to look like a regular method call.
Another option is to subclass the service and override the method(s) that you want, and replace the injected instance with that. This or something like it would be necessary if you type the dependency injection (e.g. SomeService someService). Either create a named subclass (class TestSomeService extends SomeService { ... }) or create an anonymous inner class:
def "test 1"() {
...
def mockedService = new SomeService() {
def methodToMock(args) {
return ...
}
}
myController.someService = mockedService
...
myController.save()
}
Altering the metaClass in one test will absolutely affect other tests. You're altering the groovy system, and need to perform some special cleanup if you're metaClassing. At the end my methods where I metaClass, I call a function to revoke the metaClass changes, passing in the name of the class that was metaClassed, and the instance metaClassed if there was one.
def "some authenticated method test"() {
given:
def user = new UserDomain(blah blah blah)
controller.metaClass.getAuthenticatedUser = { return user }
when:
controller.authenticatedMethod() // which references the authenticated user
then:
// validate the results
cleanup:
revokeMetaClassChanges(theControllerClass, controller)
}
private def revokeMetaClassChanges(def type, def instance = null) {
GroovySystem.metaClassRegistry.removeMetaClass(type)
if (instance != null) {
instance.metaClass = null
}
}
Alternatively, you can just mock the service in the test. A method similar to that mentioned by Burt could be:
def "some test"() {
given:
def mockSomeService = mockFor(SomeService)
mockSomeService.demand.methodToMock(1) { def args ->
return []
}
controller.someService = mockSomeService.createMock()
when:
controller.save()
then:
// implement your validations/assertions
}
I am looking for ways on how to cleanup my Grails controller code. In various controllers i more or less have the same logic..
get the object
check if it exists
etc..
Is there a suggested way on making controller actions reuse common code?
--- solution ---
All answers to the question have contributed to the solution we have implemented.
We created a class that is used in our controllers using the Mixin approach. One of the methods that the mixin exposes is the withObject method. This method takes the domainname from the controller and uses this a base for the method. This behaviour can be overridden of course!
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"
}
}
So all answers have contributed to the solution! Thanks a lot!
I always pull out this blog post when this question comes up:
http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/
Basically you have a private helper for various domains in your controllers.
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"
}
}
The way you code the getter is very flexible and a typical use for me (that is not covered in the blog) is for editing etc.
I normally code this way (i like the pattern for its clear division and readability):
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"])
}
}
With my getter helper being:
private def withIssue( Closure c) {
def issue = Issue.get(params.id)
if(issue) {
c.call issue
}
else {
response.sendError(404)
}
}
I do think that the mixin method (very similar to the 'extend a common abstract controller' way) is nice too, but this way gives two advantages:
You can type the helper, like you see I do in the closure giving you access to the methods etc in STS/IDEA (not tested Netbeans)
The repetition is not very high, and the ability to change the getter (to use for example BarDomain.findByFoo(params.id) etc)
In the view I bind to edit() I just put an id="${issue.id}" in the <g:form> and it works seamlessly.
I wouldn't recommend inheritance for that, as you can't spread generic methods in several super classes. Your abstract class would quickly become messy if you have many controllers. You can't use composition (for instance using a Service) because you don't have access to response, render, or params directly from there.
The approach I use is to inject generic methods via Mixins.
#Mixin(ControllerGenericActions)
#Mixin(ControllerUtil)
class BookController {
def show = &genericShow.curry(Book)
def exists = {
render(idExists(Book))
}
}
The first action show uses a generic method in ControllerGenericActions.groovy, with an argument binded to it. The second use of a mixin idExists method is inside a controller action.
Here is an example code for src/groovy/ControllerGenericActions.groovy
class ControllerGeneric {
def genericShow(Class clazz) {
render clazz.get(params.id) as XML
}
}
and in src/groovy/ControllerUtil.groovy
class ControllerUtil {
def idExists (Class clazz) {
return clazz.get(params.id) != null
}
Not very useful in this case, but you get the idea.
Implement abstract controller with common methods (use 'protected' directive) and extend from it your real controllers. Do not use 'get' and 'set' words at the beginning of this method's names. Not good, but it works.
Wanna do the following:
BootStrap {
def init = {servletContext ->
........
MyDomainClass.metaClass.save = {->
delegate.extraSave()
//////// how to call original save() here?
}
}
.........
}
P.S. MyDomainClass#extraSave is defined as public void extraSave(){.....}
First of all, Bootstrap.groovy may not be the best place to do this kind of metaprogramming. The problem with this approach is that the changes to the classes will be applied when the application starts, but you may lose these changes when the application is reloaded. Obviously this is only an issue during development, and not an issue at all if you don't mind restarting the server every time you make a change, but I'll bet this would quickly become a major annoyance. In order to have the changes applied when app is reloaded as well, you should move the metaprogramming into a plugin, where you can hook into the onChange application lifecycle event.
So the steps are:
Create a plugin
Do the metaprogramming in the doWithDynamicMethods and onChange closures of the plugin descriptor
Here's a complete example where I "override" the chain() method on all the controller classes. The code to do likewise for the save() method of domain classes should only require some obvious replacements, e.g. use application.domainClasses instead of application.controllerClasses
def doWithDynamicMethods = {ctx ->
application.controllerClasses.each {controller ->
replaceChain(controller)
}
}
def onChange = {event ->
if (application.isArtefactOfType(ControllerArtefactHandler.TYPE, event.source)) {
def clz = application.getControllerClass(event.source?.name)
replaceChain(clz)
}
}
private replaceChain(controllerClass) {
// Save a reference to the grails chain() method
def grailsChain = controllerClass.metaClass.pickMethod("chain", [Map] as Class[])
controllerClass.metaClass.chain = {Map params ->
println "My code to execute before chain goes here"
// Invoke the grails chain() method
grailsChain.invoke(delegate, [params] as Object[])
println "My code to execute after chain goes here"
}
}
why not leveraging the GORM events for this purpose? In the Domain class:
def extraSave() {
// ...
}
def beforeInsert = {
extraSave()
}
def beforeUpdate = {
extraSave()
}
IMHO this a cleaner approach. Documentation can be found here
Not sure if the following works, but this might be a solution:
MyDomainClass.metaClass.origSave = MyDomainClass.metaClass.save
MyDomainClass.metaClass.save = {->
delegate.extraSave()
delegate.origSave()
}
Please give me feedbeck if the above worked...
I'm writing a grails plugin and I need to hook into the domain save() method to do some logic after the save. I need to do this across multiple domain classes. I'm trying to avoid hibernate events in the cases where a plugin user is not using hibernate with GORM.
I've tried many thing but below is what I think should have had the best chance at working. In all cases grailsSave is null. How can I do this?
def doWithDynamicMethods = { ctx ->
application.domainClasses.each { dc ->
def grailsSave = dc.metaClass.pickMethod('save', [Map] as Class[])
domainClass.metaClass.save = { Map params ->
grailsSave.invoke(delegate, [params] as Object[])
println "Saved object, now do my thing"
//...
}
}
}
I have the following set in my *Plugin.groovy class:
def dependsOn = [domainClass: '1.1 > *', hibernate: '1.1 > *']
def loadAfter = ['hibernate']
I was unable to successfully get a reference to the save() methods during plugin/app initialization; I don't know why. Instead, I decided to create a listener for the hibernate events after insert, update, and deletes. This post by Sean Hartsock regarding the Audit Logging plugin was a perfect primer for doing that.
Here's the gist of the Listener:
class MyListener implements PostInsertEventListener, PostUpdateEventListener, PostDeleteEventListener, Initializable {
public void onPostInsert(final PostInsertEvent event) {
// logic after insert
return
}
public void onPostUpdate(final PostUpdateEvent event) {
// logic after update
return
}
public void onPostDelete(final PostDeleteEvent event) {
// logic after delete
return
}
public void initialize(final Configuration config) {
return
}
}
Then in the *GrailsPlugin.groovy:
def doWithApplicationContext = { applicationContext ->
// add the event listeners for reindexing on change
def listeners = applicationContext.sessionFactory.eventListeners
def listener = new MyListener()
['postInsert', 'postUpdate', 'postDelete'].each({
addEventTypeListener(listeners, listener, it)
})
}
// copied from http://hartsock.blogspot.com/2008/04/inside-hibernate-events-and-audit.html
private addEventTypeListener(listeners, listener, type) {
def typeProperty = "${type}EventListeners"
def typeListeners = listeners."${typeProperty}"
def expandedTypeListeners = new Object[typeListeners.length + 1]
System.arraycopy(typeListeners, 0, expandedTypeListeners, 0, typeListeners.length)
expandedTypeListeners[-1] = listener
listeners."${typeProperty}" = expandedTypeListeners
}
Fairly simple at the end of the day...
There are three different version of save added to the metaClass,
save(Map)
save(Boolean)
save()
Which one are you calling in your testing? You'll need to add you code to each one.
Another thing to check is whether your plugin is running after the hibernate plugin which adds the three methods to the metaClass
cheers
Lee
Have a look at the Falcone Util plugin. This plugin allows you to hook into Hibernate events (see documentation at the bottom of the page). I don't know if this is exactly what you want, but you might get some hints.
Ps! I don't think the plugin works with Grails 1.2 yet.
This is an issue of premature optimization: older versions of Groovy seriously penalized MetaClass mangling, and so GORM does not add all of its magic until it detects the need to.
Easiest solution is to have your plugin dependOn GORM Labs (I work around it there). The alternative solution is to trigger methodMissing manually (which would be duplicating the work I did). See the GORM Labs documentation for details on how I accomplished that.
Wouldn't this best be added to the service class that owns the unit of work? That's where the usual Spring/Grails idiom would have such logic. You needn't modify the save at all.
Additional GORM methods are lazily initialized on first call to any of them.
To initialize them in doWithDynamicMethods simply call one of the static methods on your domain class(es):
def doWithDynamicMethods = { ctx ->
application.domainClasses.each { dc ->
// call any static method to initialize dynamic gorm methods
dc.clazz.count()
def grailsSave = dc.metaClass.pickMethod('save', [Map] as Class[])
//...
}
}
Your save() method will be available now. As this is called at start up a single count shouldn't be to much of a problem.