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
Related
I'm working with Grails 2.4.5 and struggling to get Grails to delineate different exception types.
Suppose I want to mock the below:
class FooController {
def barService
...
def fooAction() {
try {
barService.someMethod(params)
} catch(e) {
if (e instanceof FooException) { ... }
else if (e instanceof BarException) { ... }
else { ... }
}
}
Given the test below
#TestFor(FooController)
class FooControllerSpec extends Specification {
def setup() { controller.barService = Mock(BarService) }
void "test"() {
given: "a mock dependency"
1* controller.barService.someMethod(_) >> { -> throw FooException('foo') }
when: "the action is requested"
controller.fooAction()
then: "expect the FooException behaviour from the action"
// some behaviour
}
I would expect the FooException within the mock dependency closure to have thrown
however, debugging shows the below instead:
groovy.lang.MissingMethodException: No signature of method: somePackage.FooControllerSpec$_$spock_feature_0_9_closure14.doCall() is applicable for argument types: (java.util.Arrays$ArrayList) values: [[[:]]]
Is this a bug? is there a way to mock different Exceptions in the fashion described above?
You need to specify behaviour in the then block, ie:
void "test"() {
when: "the action is requested"
controller.fooAction()
then: "expect the FooException behaviour from the action"
1 * controller.barService.someMethod(_) >> { -> throw new FooException('foo') }
response.redirectedUrl == "/some/other/path"
}
I have a Grails project and I need to send/save table data. I have a controller(doesn't have views) with following code.
class JsonController {
def getCompany = {
for (String s in request.getHeaderNames()) {
println request.getHeader(s)
}
println Company.list()
render Company.list() as XML
}
def getEmployees = {
for (String s in request.getHeaderNames()) {
println request.getHeader(s)
}
render Employees.list() as XML
}
def getManagers = {
for (String s in request.getHeaderNames()) {
println request.getHeader(s)
}
render Managers.list() as XML
}
}
Now I need to call/run these functions on clicking an link and send the output thru email or save to a folder. How can I do this?
Thankyou
Note: the following code requires the mail plugin
I would move your JsonController to a service, which I renamed to XMLService, since that is what you are returning. Then, inject the XMLService in whatever artefacts you want to use it in, such as another service or controller.
import grails.converters.XML;
class XMLService {
def getCompanies = {
return Company.list() as XML
}
def getEmployees = {
return Employees.list() as XML
}
def getManagers = {
return Managers.list() as XML
}
}
Then, in a controller or service. This example uses a service:
class MyMailingService {
def mailService //<-- included from the Mail plugin
def xmlService
void sendEmployeeList(){
mailService.sendMail {
to "fred#g2one.com","ginger#g2one.com"
subject "Hello to mutliple recipients"
body xmlService.getEmployees()
}
}
}
And for storing files, something like the following. Note, that this does not have to be service, but for demonstration purposes, it was easier.
class MyStorageService {
def xmlService
void storeEmployeeList(){
//It was a little unclear how you wanted to store the file, so be careful because this this does not include checks you would want to implement in production (e.g. checks for existing files, possible runtime exceptions, etc).
def f= new File('employees.txt') //see http://docs.codehaus.org/display/GROOVY/JN2015-Files
f << xmlService.getEmployees()
}
}
Noticed you have had answers for email text file - this is how to to store to XML file
where rowid will be the definition for each xml row
def file=""${System.properties['catalina.base']}/file.xml"
try {
new File(file).withWriter { writer ->
def xml = new MarkupBuilder( writer )
def Users = Registeration.list()
xml.mkp.xmlDeclaration(version: "1.0", encoding: "utf-8")
xml.rowid {
Employees.each { employee ->
xml.registeration(id: employee.id) {
username(employee.username)
//somethingelse(employee.somethingelse)
}
}
}
}
} catch (Exception e) {
result=e.printStackTrace()
}
if (result!=null) {
result="all done stored in "+file
}else{
result="Something has gone wrong with "+file
}
Grails 1..3.7
In regular controller actions I can do the following:
def someAction = { OneCommand oneCmd, TwoCommand twoCmd ->
}
However, in a webflow action I have the equivillent:
def someFlow = {
someAction {
on("something") { OneCommand oneCmd, TwoCommand twoCmd ->
}
}
}
which doesn't bind the params to either object. If I remove one of them, it binds to the one left. Is this a bug or expected behavior?
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 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)
}