Grails In Action是使用Grails技术构建一个名叫Hubbub的博客网站,下面是第三章的代码。
第三章主要讲解构建hubbub博客需要的domain对象,重点是模型对象关系,涉及到的知识点如下:
User.groovy
package com.grailsinaction
class User {
String loginId
String password
Date dateCreated
static hasOne = [ profile : Profile ]
static hasMany = [ posts : Post, tags : Tag, following : User ]
static constraints = {
loginId size: 3..20, unique: true, blank: false
password size: 6..8, blank: false
profile nullable: true
}
static mapping = {
profile lazy: false
}
}
Profile.groovy
package com.grailsinaction
class Profile {
User user
byte[] photo
String fullName
String bio
String homepage
String email
String timezone
String country
String jabberAddress
static constraints = {
fullName blank: false
bio nullable: true, maxSize: 1000
homepage url: true, nullable: true
email email: true, nullable: false
photo nullable: true
country nullable: true
timezone nullable: true
jabberAddress email: true, nullable: true
}
}
Post.groovy
package com.grailsinaction
class Post {
String content
Date dateCreated
static belongsTo = [ user : User ]
static hasMany = [ tags : Tag ]
static constraints = {
content blank: false
}
static mapping = {
sort dateCreated: "desc"
}
}
Tag.groovy
package com.grailsinaction
class Tag {
String name
User user
static hasMany = [ posts : Post ]
static belongsTo = [ User, Post ]
static constraints = {
name blank: false
}
}
ApplicationUser.groovy
package com.grailsinaction
class ApplicationUser {
String applicationName
String password
String apiKey
static constraints = {
importFrom User, include: ['password']
applicationName blank: false, unique: true
apiKey blank: false
}
}
UserIntegrationSpec.groovy
package com.grailsinaction
import grails.plugin.spock.IntegrationSpec
class UserIntegrationSpec extends IntegrationSpec {
def "Saving our first user to the database"() {
given: "A brand new user"
def joe = new User(loginId: 'joe', password: 'secret')
when: "the user is saved"
joe.save()
then: "it saved successfully and can be found in the database"
joe.errors.errorCount == 0
joe.id != null
User.get(joe.id).loginId == joe.loginId
}
def "Deleting an existing user removes it from the database"() {
given: "An existing user"
def user = new User(loginId: 'joe', password: 'secret').save(failOnError: true)
when: "The user is deleted"
def foundUser = User.get(user.id)
foundUser.delete(flush: true)
then: "The user is removed from the database"
!User.exists(foundUser.id)
}
def "Saving a user with invalid properties causes an error"() {
given: "A user which fails several field validations"
def user = new User(loginId: 'chuck_norris', password: 'tiny')
when: "The user is validated"
user.validate()
then:
user.hasErrors()
"size.toosmall" == user.errors.getFieldError("password").code
"tiny" == user.errors.getFieldError("password").rejectedValue
!user.errors.getFieldError("loginId")
// 'homepage' is now on the Profile class, so is not validated.
}
def "Recovering from a failed save by fixing invalid properties"() {
given: "A user that has invalid properties"
def chuck = new User(loginId: 'chuck_norris', password: 'tiny')
assert chuck.save() == null
assert chuck.hasErrors()
when: "We fix the invalid properties"
chuck.password = "fistfist"
// 'homepage' is now on Profile so can't be set on the user.
then: "The user saves and validates fine"
chuck.validate()
!chuck.hasErrors()
chuck.save()
}
def "Ensure a user can follow other users"() {
given: "A set of baseline users"
def glen = new User(loginId: 'glen', password:'password').save()
def peter = new User(loginId: 'peter', password:'password').save()
def sven = new User(loginId: 'sven', password:'password').save()
when: "Glen follows Peter, and Sven follows Peter"
glen.addToFollowing(peter)
glen.addToFollowing(sven)
sven.addToFollowing(peter)
then: "Follower counts should match following people"
2 == glen.following.size()
1 == sven.following.size()
}
}
PostIntegrationSpec.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()
}
}
ApplicationUserIntegrationSpec.groovy
package com.grailsinaction
import grails.plugin.spock.IntegrationSpec
class ApplicationUserIntegrationSpec extends IntegrationSpec {
def "Recovering from a failed save by fixing invalid properties"() {
given: "A user that has invalid properties"
def chuck = new ApplicationUser(applicationName: 'chuck_bot', password: 'tiny', apiKey: 'cafebeef00')
assert chuck.save() == null
assert chuck.hasErrors()
when: "We fix the invalid properties"
chuck.password = "fistfist"
chuck.save(failOnError: true)
then: "The user saves and validates fine"
chuck.validate()
!chuck.hasErrors()
chuck.save()
println chuck.findAll()
}
}
地址