29 февраля 2012 г.

Spring Web Flow и Grails 2

Предположим бизнесс-процесс вашего приложения требует от пользователя последовательности действий, которые он должен совершить. К примеру, в интернет-магазинах мы не можем посмотреть страницу благодарности за покупку не сделав этой самой покупки. Или мы не можем посмотреть какой-либо отчет, не указав критерии данных, которые должны войти в отчет. Для простых действий всё довольно тривиально, но если шагов перед завершением процесса должно быть множество, то мы рискуем превратить наш код в вермишель из хитросплетений вызовов и проверок. Для того чтобы этого избежать существует замечательный фреймворк - Spring Web Flow. И в этой заметке я приведу пример его использования в Grails 2.0.1.


"Сколько людей - столько и мнений" любила повторять моя школьная учительница по английскому языку. Мнения - информция. "Кто владеет информацией - владеет миром". Вот и напишем приложение, которое будет накапливать в себе мнения людей по определенным вопросам. :) Причем, чтобы мнения были независимы, пусть пользователи не могут узнать мнения других людей, пока не выскажут свое.

Таким образом бизнесс-процесс нашего приложения примерно следующий:
  1. Узнать сколько уже существует мнений (чтобы пользователь знал, что после его участия в опросе, он сможет прочитать сотни мнений других людей)
  2. Показать пользователю страничку, содержащую поле ввода его мнения, кнопку его отправки и счетчик существующих мнений
  3. Если пользователь всё сделал правильно - поблагодарить его и показать список существующих мнений

Конечно, нужно было бы предусмотреть хоть какие-либо случаи, когда что-то пойдет не так. Но это же простейшая реализация flow - так что остановимся на этом.

Приступим к реализации. Предпологаю, что фреймворк Grails у вас уже скачан и распакован, GRAILS_HOME и JAVA_HOME указывают в нужные каталоги и PATH подправлен.

Начало
В консоли вводим:
grails create-app simpleFlow
В результате выполнения этой строки мы получаем заготовку нашего будущего приложения. В Grails 2 поддержка Web Flow по умолчанию выключена. Чтобы это исправить подключим плагин webflow, введя комманду:
grails install-plugin webflow
Доменный уровень
С какими сущностями работает наше приложение? Да собственно только с одной - cообщение. Вот и создадим доменный класс Message:
grails create-domain-class simpleflow.Message
Добавим к этому классу поле text. Пусть его длина будет больше 5, дабы если и писали матюки, то хоть изощренные, и меньше 100.
Получим следующее:
class Message implements Serializable {
    String text
    static constraints = {
        text size: 5..100
    }
}
Контроллеры
Тут - самое интересное. Создадим контроллер, который будет обеспечивать работу нашего процесса. Назовем его SimpleController:
grails create-controller simpleflow.Simple
Описываем приведенный выше процесс. Начальным состоянием - состоянием, в котором будет процесс, когда пользователь обратиться к нему - будет entryPoint. Конечным состоянием - displayList. На самом деле описание процесса более чем тривиально, так что просто приведу код класса SimpleController, который получился у меня:
class SimpleController {
    def index() { 
      redirect(action:'world')
    }
 
    def worldFlow = {
        entryPoint {
            action {
                [count:Message.count()]
            }
            on("success").to "enterMessage"
        }
        enterMessage {
            on("submit") {
                def m = new Message(text:params.text)
                if(!m.validate()) return error()
                m.save()
            }.to "getList"
        }
        getList {
            action {
                [messageList:Message.list()]
            }
            on("success").to "displayList"
        }
        displayList()
    }
}
entryPoint и getList - это action-состояния. У них нет визуального отображения - они нужны только для того, чтобы сделать какие-либо действия. Для enterMessage и displayList создадим 2 gsp-страницы.

Представления
1. Страница views/simple/world/enterMessage.gsp:
<%@ page contentType="text/html;charset=UTF-8" %>

<html>
    <head>
        <meta name="layout" content="main">
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>What do you think about this world?</title>
    </head>
    <body>
        <div class="content" role="main">
            <h1>What do you think about this world?</h1>
            <g:form action="world" method="post">
                <fieldset class="form">
                    <input type="text" name='text'/>
                    <g:submitButton name="submit" value="Submit"/>
                    <br /><small>We have ${count} opinions!</small>
                </fieldset>
            </g:form>
        </div>
    </body>
</html>
2. Страница view/simple/world/displayList.gsp:
<%@ page contentType="text/html;charset=UTF-8" %>

<html>
    <head>
        <meta name="layout" content="main">
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Our meanings</title>
    </head>
    <body>
        <div class="content" role="main">
            <h1>Thank you!</h1>
            Here is what other peoples think about this world:
            <table>
                <thead>
                    <tr>
                        <g:sortableColumn property="text" title="Meanings" />
                    </tr>
                </thead>
                <tbody>
                    <g:each in="${messageList}" status="i" var="message">
                        <tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
                            <td>
                                ${fieldValue(bean: message, field: "text")}
                            </td>
                        </tr>
                    </g:each>
                </tbody>
            </table>
        </div>
    </body>
</html>
Всё! Делаем grails run-app, вводим в браузере http://localhost:8080/SimpleFlow/simple/world и играемся результатом. Конечно, это с очень большой натяжкой можно назвать законченым результатом, но главное мы уже сделали - подружили Grails и Spring Web Flow. :)

Комментариев нет:

Отправить комментарий

Примечание. Отправлять комментарии могут только участники этого блога.