文档章节

Grails In Action-06.Controlling application flow

寡鸡蛋
 寡鸡蛋
发布于 2013/11/26 15:30
字数 966
阅读 210
收藏 2

第六章主要讲解使用controller实现页面之间的跳转。在系统中实现了Post信息提交及显示功能。主要的知识点有:

  • 访问路径控制:http://url:port/app/controller/action/params
  • addPost方法:timeline页面的form将表单提交给addPost方法,addPost方法返回
  • .操作符:list.prop
  • ?:操作符:表达式?如果true:如果false
  • ${flash.massage}
  • spock单元测试:controller页面重定向测试
  • spock单元测试:@spock.lang.Unroll
  • spock单元测试:"Testing id of #suppliedId redirects to #expectedUrl"
  • spock集成测试

默认访问index,重定向给timeline/params,如果没有params,默认给一个.

<!-- lang: groovy -->
......
def index() {
    if (!params.id) 
        params.id = "jeff"
        redirect action: 'timeline', params: params
    }

def timeline() {
    def user = User.findByLoginId(params.id)
    if (!user) {
        response.sendError(404)
    } else {
        [ user : user ]
    }
}
......

增加一个post,因为post需要指定user,所以需要user登录,前面的index指定了一个user params,可以直接使用.后续增加登录功能

<!-- lang: groovy -->
......
def addPost() {
    def user = User.findByLoginId(params.id)
    if (user) {
        def post = new Post(params)
        user.addToPosts(post)
        if (user.save()) {
            flash.message = "Successfully created Post"
        } else {
            flash.message = "Invalid or empty post"
        }
    } else {
        flash.message = "Invalid User Id"
    }
    redirect(action: 'timeline', id: params.id)
}
......

访问index,重定向给timeline,timeline.gsp负责提交post并显示post信息,所以,timeline分这两个部分.

<!-- lang: groovy -->
<html>
    <head>
        <title>Timeline for ${ user.profile ? user.profile.fullName : user.loginId }</title>
        <meta name="layout" content="main"/>
    </head>

    <body>
        <div id="newPost">
            <h3>
                What is ${ user.profile ? user.profile.fullName : user.loginId } hacking on right now?
            </h3>

            <g:if test="${flash.message}">
                <div class="flash">${flash.message}</div>
            </g:if>

            <@!-- 提交(addPost) -->
            <p>
                <g:form action="addPost" id="${params.id}">
                    <g:textArea id='postContent' name="content" rows="3" cols="50"/><br/>
                    <g:submitButton name="post" value="Post"/>
                </g:form>
            </p>
        </div>

        <@!-- 显示(timeline/params) -->
        <div class="allPosts">
            <g:each in="${user.posts}" var="post">
                <div class="postEntry">
                    <div class="postText">${post.content}</div>
                    <div class="postDate">${post.dateCreated}</div>
                </div>
            </g:each>
        </div>
    </body>
</html>

这本书在测试上下了一些功夫,讲解的也很到位. 单元测试:test/unit/com.grailsinaction.PostControllerSpec.groovy

<!-- lang: groovy -->
package com.grailsinaction

import grails.test.mixin.Mock
import grails.test.mixin.TestFor

import spock.lang.Specification

@TestFor(PostController)
@Mock([User,Post])
class PostControllerSpec extends Specification {
    def "Get a users timeline given their id"() {
        given: "A user with posts in the db"
            User chuck = new User(loginId: "chuck_norris", password: "password").save(failOnError: true)
            chuck.addToPosts(new Post(content: "A first post"))
            chuck.addToPosts(new Post(content: "A second post"))

        and: "A loginId parameter"
            params.id = chuck.loginId

        when: "the timeline is invoked"
            def model = controller.timeline()

        then: "the user is in the returned model"
            model.user.loginId == "chuck_norris"
            model.user.posts.size() == 2
    }

    def "Check that non-existent users are handled with an error"() {

        given: "the id of a non-existent user"
            params.id = "this-user-id-does-not-exist"

        when: "the timeline is invoked"
            controller.timeline()

        then: "a 404 is sent to the browser"
            response.status == 404

    }

    def "Adding a valid new post to the timeline"() {
        given: "A user with posts in the db"
            User chuck = new User(loginId: "chuck_norris", password: "password").save(failOnError: true)

        and: "A loginId parameter"
            params.id = chuck.loginId

        and: "Some content for the post"
            params.content = "Chuck Norris can unit test entire applications with a single assert."

        when: "addPost is invoked"
            def model = controller.addPost()

        then: "our flash message and redirect confirms the success"
            flash.message == "Successfully created Post"
            response.redirectedUrl == "/post/timeline/${chuck.loginId}"
            Post.countByUser(chuck) == 1
    }

    def "Adding a invalid new post to the timeline trips an error"() {
        given: "A user with posts in the db"
            User chuck = new User(loginId: "chuck_norris", password: "password").save(failOnError: true)

        and: "A loginId parameter"
            params.id = chuck.loginId

        and: "Some content for the post"
            params.content = null

        when: "addPost is invoked"
            def model = controller.addPost()

        then: "our flash message and redirect confirms the success"
            flash.message == "Invalid or empty post"
            response.redirectedUrl == "/post/timeline/${chuck.loginId}"
            Post.countByUser(chuck) == 0

    }

    @spock.lang.Unroll
    def "Testing id of #suppliedId redirects to #expectedUrl"() {
        given:
            params.id = suppliedId

        when: "Controller is invoked"
            controller.index()

        then:
            response.redirectedUrl == expectedUrl

        where:
            suppliedId | expectedUrl
            'joe_cool' | '/post/timeline/joe_cool'
            null | '/post/timeline/chuck_norris'
    }
}

集成测试:test/integration/com.grailsinaction.PostIntegrationSpec.groovy

<!-- lang: groovy -->
package com.grailsinaction

import grails.plugin.spock.IntegrationSpec

class PostIntegrationSpec extends IntegrationSpec {
	
	def "Adding posts to user links post to user"() {

		given: "A brand new user"
			def user = new User(loginId: 'joe',
				password: 'secret').save(failOnError: true)

		when: "Several posts are added to the user"
			user.addToPosts(new Post(content: "First post... W00t!"))
			user.addToPosts(new Post(content: "Second post..."))
			user.addToPosts(new Post(content: "Third post..."))

		then: "The user has a list of posts attached"
			3 == User.get(user.id).posts.size()
	}


	def "Ensure posts linked to a user can be retrieved"() {

		given: "A user with several posts"
			def user = new User(loginId: 'joe', password: 'secret').save(failOnError: true)
			user.addToPosts(new Post(content: "First"))
			user.addToPosts(new Post(content: "Second"))
			user.addToPosts(new Post(content: "Third"))

		when: "The user is retrieved by their id"
			def foundUser = User.get(user.id)
			List<String> sortedPostContent = foundUser.posts.collect { it.content }.sort()

		then: "The posts appear on the retrieved user"
			sortedPostContent == ['First', 'Second', 'Third']
	}

	def "Exercise tagging several posts with various tags"() {

		given: "A user with a set of tags"
			def user = new User(loginId: 'joe', password: 'secret').save(failOnError: true)
			def tagGroovy = new Tag(name: 'groovy')
			def tagGrails = new Tag(name: 'grails')
			user.addToTags(tagGroovy)
			user.addToTags(tagGrails)

		when: "The user tags two fresh posts"
			def groovyPost = new Post(content: "A groovy post")
			user.addToPosts(groovyPost)
			groovyPost.addToTags(tagGroovy)

			def bothPost = new Post(content: "A groovy and grails post")
			user.addToPosts(bothPost)
			bothPost.addToTags(tagGroovy)
			bothPost.addToTags(tagGrails)

		then:
			user.tags*.name.sort() == ['grails', 'groovy']
			1 == groovyPost.tags.size()
			2 == bothPost.tags.size()
	}
}

© 著作权归作者所有

共有 人打赏支持
寡鸡蛋
粉丝 12
博文 51
码字总数 19107
作品 0
中山
售前工程师
私信 提问
Grails 1.2参考文档速读系列

一个很好的Grails中文参考资料。 Grails 1.2参考文档速读(1):第1、2章 Grails 1.2参考文档速读(2):配置基础和环境 Grails 1.2参考文档速读(3):日志配置 Grails 1.2参考文档速读(4...

groovyland
2010/02/25
359
1
When Monolithic Is the Way: Play vs. Spring Boot vs. Grails

Nowadays, when we are going to start a new project, the developer starts thinking about the technology, and usually they come up with something like: microservices, RESTFul, Act......

Marini Fabio
2017/12/20
0
0
SpringSource Tool Suite 2.6.0 发布

SpringSource Tool Suite 今天发布了 2.6.0 版本,主要值得关注的改进包括: All the updates that you got from 2.5.2.SR1 included (Spring Roo 1.1.2, Eclipse Helios SR2, Groovy 1.7.8......

红薯
2011/03/19
444
1
Keycloak 1.8.0.CR1 发布,SSO 集成解决方案

Keycloak 1.8.0.CR1 发布,更新如下: Default Admin User Removed - we no longer have a built in admin account, instead a new account has to be created initially from http://local......

oschina
2016/01/14
990
1
Ls 1 - Understanding the nature of Spark Streaming

What is Spark Streaming? According to the Official Apache Spark website, Spark Streaming is an extension of the core Spark API that enables scalable, high-throughput, fault-tole......

jcchoiling
2016/05/09
738
0

没有更多内容

加载失败,请刷新页面

加载更多

c++中友元函数理解与使用

在学习c++这一块,关于友元函数和友元类,感觉还是不好理解,但是井下心来,理解,需要把我一下几点。 首先讲友元函数。 (1)友元函数: 1)C++中引入友元函数,是为在该类中提供一个对外(除...

天王盖地虎626
18分钟前
0
0
OSChina 周一乱弹 —— 白掌柜说了卖货不卖身

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @爱漫爱 :这是一场修行分享羽肿的单曲《Moony》 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :开不开心? 开心呀, 我又不爱睡懒觉…...

小小编辑
今天
47
3
大数据教程(11.7)hadoop2.9.1平台上仓库工具hive1.2.2搭建

上一篇文章介绍了hive2.3.4的搭建,然而这个版本已经不能稳定的支持mapreduce程序。本篇博主将分享hive1.2.2工具搭建全过程。先说明:本节就直接在上一节的hadoop环境中搭建了! 一、下载apa...

em_aaron
今天
5
0
开始看《JSP&Servlet学习笔记》

1:WEB应用简介。其中1.2.1对Web容器的工作流程写得不错 2:编写Servlet。搞清楚了Java的Web目录结构,以及Web.xml的一些配置作用。特别是讲了@WebServlet标签 3:请求与响应。更细致的讲了从...

max佩恩
今天
6
0
mysql分区功能详细介绍,以及实例

一,什么是数据库分区 前段时间写过一篇关于mysql分表的的文章,下面来说一下什么是数据库分区,以mysql为例。mysql数据库中的数据是以文件的形势存在磁盘上的,默认放在/mysql/data下面(可...

吴伟祥
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部