Grails WebFlow DRY Branching Logic - grails

Grails WebFlow noob here...
One state in my WebFlow receives two events that need to trigger the same action and then transition to separate states if that action is successful. My initial attempt repeated the code in the actionState. Not good. So, after some trial and error, I came up with the following.
state0 {
on("event1") {
flash.stateAfterNext = "state1"
}.to "actionState"
on("event2") {
flash.stateAfterNext = "state2"
}.to "actionState"
}
actionState {
action {
flow.unit = Unit.get(params.unit)
success()
}
on("success").to { flash.stateAfterNext }
on(Exception).to "home"
}
state1 { ... }
state2 { ... }
This works but is it good Grails practice? Is there a better way to handle flow branching logic like this? In particular, should I have used a subflow here, and, if so, what would that look like?
Note: I attempted to move the code in actionState into a separate method but since it references flow that didn't work.

What about something like
flow{
state0 {
on("event1") {
saveUnit(flow,params.unit)
}.to "state1"
on("event2") {
saveUnit(flow,params.unit)
}.to "state2"
}
state1{...}
state2{...}
}
private saveUnit(flow, unit){
flow.unit = Unit.get(unit)
}

Related

Reusing part of Grails criteria closure

I have a fairly large criteria closure in my Grails application, and I would like to reuse part of it in several places in my application. Rather than duplicating the section I need to reuse, I'd like to define this as a separate closure and reference it wherever it is needed, but I am struggling a bit with the syntax.
This is a simplified / cut down version, but essentially my criteria looks something like this:
def criteriaClosure = {
and {
// filtering criteria that I'd like to reuse in lots of places
or {
names.each { name ->
sqlRestriction(getFilteringSql(name), [someId])
}
}
if (isOrganisationChild(childDefaultGrailsDomainClass)) {
sqlRestriction(getFilteringSql(domain), [someArg])
}
// filtering criteria that's specific to this particular method
sqlRestriction(getSomeOtherSql(), [someOtherArg])
}
}
def criteria = domain.createCriteria()
def paginatedList = criteria.list([offset: offset, max: max], criteriaClosure)
I've tried defining the part of the closure I want to reuse as a variable, and referencing it in my criteria closure, however the restrictions it defines don't seem to be applied.
def reusableClosure = {
and {
or {
names.each { name ->
sqlRestriction(getFilteringSql(name), [someId])
}
}
if (isOrganisationChild(childDefaultGrailsDomainClass)) {
sqlRestriction(getFilteringSql(domain), [someArg])
}
}
}
def criteriaClosure = {
and {
reusableClosure() //this doesn't seem to work
sqlRestriction(getSomeOtherSql(), [someOtherArg])
}
}
I'm sure this must be a pretty straightforward thing to do, so apologies if it's a daft question. Any ideas?
I think you have to pass the delegate down to the reusableClosure, ie:
def criteriaClosure = {
and {
reusableClosure.delegate = delegate
reusableClosure()
sqlRestriction(getSomeOtherSql(), [someOtherArg])
}
}

Grails 2 - How to dynamically call multiple datasources

I have two named data sources in my Grails app (Grails 2.0.3)...
dataSource_a {
// ...
}
dataSource_b {
// ...
}
I'd like the ability to dynamically change what datasource I'm accessing, based on some kind of parameter. I could do something like this...
def findPeople(datasource) {
if (datasource == 'a') {
return Person.a.list()
} else if (datasource == 'b') {
return Person.b.list()
}
}
What I was really hoping to be able to do, though, is something like this...
def findPeople(datasource) {
return Person."$datasource".list()
}
Unfortunately, I get an error when I try and do that. "Fatal error occurred apply query transformations: null 1 error".
Any thoughts on how to accomplish this? Or am I just stuck with if/switch blocks?
I figured it out, this is how you have to do it.
def findPeople(datasource) {
def p = People.class
p."${datasource}".list()
}
For some reason, if you call it like that, it works.

Grails Webflow: Access flow scope outside of action or transition states

I want to call a subflow where the controller is not known. It is passed in parameters to beginFlow and I save that in flow scope. Inside goToForm I'd like to call use the controller that is saved in flow.theController.
def beginFlow = {
enter {
action {
if (params?.redirectTo != null) {
String flow.theController = params.redirectTo
}
if ( flow.theController() ) {
success()
}
}
on("success").to("beginPage")
}
beginPage {
on('next').to('goToForm')
}
goToForm {
// I'd like this:
// subflow(controller: flow.theController, action:'start'
// this subflow works, but won't work for all cases
subflow(controller: 'AZ_A4', action:'start')
on('done').to('showResults')
on('notDone').to('beginPage')
}
showResults {
redirect(action: 'results')
}
}
As discussed on the user list, it appears that this isn't possible directly, as the subflow name has to be known at the time when the flow structure is being built (at application startup). But since the flow definition DSL is Groovy code you can do something like this:
beginPage {
on('next').to('selectSubflow')
}
selectSubflow {
action {
return "subflow_${flow.theController}"()
}
for(subController in listOfControllers) {
on("subflow_${subController}").to("subflow_${subController}")
}
}
for(subController in listOfControllers) {
"subflow_${subController}" {
subflow(controller:subController, action:'start')
on('done').to('showResults')
on('notDone').to('beginPage')
}
}
The listOfControllers could be a static somewhere, or you could possibly do something like this at the top of the flow definition
def beginFlow = {
def listOfControllers = grailsApplication.controllerClasses.findAll {
it.flows.containsKey('start')
}.collect { it.logicalPropertyName }
enter {
// ...
to enumerate all the controllers in the application that define a startFlow. You might need a def grailsApplication in your class, I always forget which places in Grails have it available by default and which don't...

Grails WebFlow State Name

Grails noob here...
How do I get the state name inside a Grails webflow state? I'm prototyping a mobile app using Grails WebFlow and jQueryMobile. Because it's a mobile app comprised primarily of lists, I manage the back events using a stack like this:
class myController {
def myFlow {
start {
action {
flow.states = []
[ ... ]
}
on("success").to "state0"
}
state0 {
on("back").to "home"
on("event") {
flow.states << "state0"
}.to "state1"
}
state1 {
on("back").to { flow.states.pop() }
on("event") {
flow.states << "state1"
}.to "state2"
}
state2 {
on("back").to { flow.states.pop() }
}
home {
redirect( ... )
}
}
}
This works, but I'd like to replace the hard coded state name strings in lines like flow.states << "state#" with an expression if there's a way to do it.
EDIT: I'll accept answers that explain why this can't be done.
Try using the RequestContext and/or the FlowExecutionContext? e.g. flowExecutionContext.currentState.id

"Overloading" standard GORM CRUD methods

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...

Resources