文档章节

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
0
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 mcehref="htt......

oschina
2016/01/14
975
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
weblogic部署报错,jpa+spring

无法访问所选应用程序。 Error loading the persistence descriptorWEB-INF/classes/META-INF/persistence.xml from the module webframework. See thefollowing stack trace for nested err......

古怪945
2015/05/29
689
2

没有更多内容

加载失败,请刷新页面

加载更多

如何处理JavaScript 中的货币值?

 金钱无处不在。   无论在银行应用程序、电子商务网站还是证券交易所平台,我们每天都在与金钱互动。我们也越来越依赖技术来处理问题。   然而,关于如何以编程处理货币价值尚无共识。虽...

数据星河
17分钟前
3
0
并发中的volatile

1. 概述 由于线程有本地内存的存在, 一个线程修改的共享变量不会及时的刷新到主内存中, 使得另一个线程读取共享变量时读取到的仍旧是旧值, 就导致了内存可见性问题. 现在volatile就可以解决这...

Ala6
18分钟前
6
0
三大特性之---封装

封装从字面上来理解就是包装的意思,专业点就是信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能...

architect刘源源
21分钟前
2
0
设计模式 之 策略模式

设计模式 之 策略模式 定义 提供几个算法策略,选择其中一个策略去执行。 优点 由于将算法封装成单独的策略,策略可以灵活切换。 扩展性好,符合开闭原则。 缺点 策略多,类也会变多 策略类需...

GMarshal
22分钟前
2
0
HBase集群监控的那些事儿

为什么需要监控? 为了保证系统的稳定性,可靠性,可运维性。 掌控集群的核心性能指标,了解集群的性能表现; 集群出现问题时及时报警,便于运维同学及时修复问题; 集群重要指标值异常时进行...

微笑向暖wx
22分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部