grailsinaction / graina2 Goto Github PK
View Code? Open in Web Editor NEWSource code for the 2nd edition of Grails in Action
Source code for the 2nd edition of Grails in Action
This is a placeholder issue to allow users to suggest particular points to cover, tips & tricks, or anything else they feel has value in the book with regard to the Maven and Gradle coverage.
BootStrap.createSampleData()
changes the dateCreated
values of Post instances using GDK Date.updated()
, but it uses an invalid index name, dayOfMonth
, which is silently ignored.
Must use date
(string) or Calendar.DAY_OF_MONTH
according to http://groovy.codehaus.org/groovy-jdk/java/util/Calendar.html#set(java.util.Map)
One of the example test cases in chapter 7 looks like this:
def "Adding new post via mocked service layer honours functionality"() {
given: "a mock post service"
def mockPostService = Mock(PostService) #1
1 * mockPostService.createPost(_, _) >> new Post(content: "Mock Post") #2
controller.postService = mockPostService #3
when: "controller is invoked"
def result = controller.addPost("joe_cool", "Posting up a storm") #4
then: "redirected to timeline, flash message tells us all is well"
flash.message ==~ /Added new post: Mock.*/ #5
response.redirectedUrl == '/post/timeline/joe_cool' #6
}
As the call to createPost()
is a required interaction and should be verified, it makes sense to move it into the 'then:' block like so:
then: "..."
1 * mockPostService.createPost(_, _) >> new Post(content: "Mock Post")
flash.message ==~ /Added new post: Mock.*/
response.redirectedUrl == '/post/timeline/joe_cool'
To be honest, we should also be verifying the content parameter of the createPost()
call! So perhaps the interaction line should be:
when: "controller is invoked"
def postContent = "Posting up a storm"
def result = controller.addPost("joe_cool", postContent) #4
then: "redirected to timeline, flash message tells us all is well"
1 * mockPostService.createPost(_, postContent) >> new Post(content: postContent)
Code for ch5 downloaded from github have this in grails-app/conf/BootStrap.groovy:
private createAdminUserIfRequired() {
if (!User.findByLoginId("admin")) {
println "Fresh Database. Creating ADMIN user."
def profile = new Profile(email: "[email protected]")
new User(loginId: "admin", password: "secret", profile: profile).save()
}
else {
println "Existing admin user, skipping creation"
}
}
The effect of this is that it fails later on in createSampleData() at the time of the first save() with flush:true.
| Error 2013-07-10 15:55:37,978 [localhost-startStop-1] ERROR hibernate.AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
Message: null id in com.grailsinaction.Profile entry (don't flush the Session after an exception occurs)
At that point, it is not very easy to see what went wrong as the line where the failure occurs is far from the line having the root cause.
Note that adding failOnError: true in the save() call on admin user won't trigger the exception; only when hibernate actually tries to commit the session to database will it throw the error.
Adding fullName: "Administrator"
to creation of profile fixes the issue. I would suggest also using "[email protected]" for the email to be consistent with rest of data created in bootstrap and to add failOnError:true, flush:true
to the save() call.
This post is going to ramble a bit.
The QueryIntegrationSpec test class in this listing failed on the "Query against a range value" method. Looking at the sample data in Bootstrap.groovy, I concluded that the assertion needed to change
from: users*.loginId == ["phil", "peter", "glen", "frankie", "chuck_norris", "admin"]
to: users*.loginId == ["phil", "jeff", "graeme", "frankie", "burt", "admin"]
I wasn't getting the admin user, so after adding some println's I found that the save for admin was failing silently. To get it to work, I changed the code
from:
def profile = new Profile(email: "[email protected]")
new User(loginId: "admin", password: "secret", profile: profile).save()
to:
def profile = new Profile(fullName: "Administrator", email: "[email protected]")
def user = new User(loginId: "admin", password: "secret", profile: profile, dateCreated: new Date())
user.save(failOnError: true, flush: true)
println "Created user " + user + " dateCreated=" + user.dateCreated
I also added a println to the createSampleData() method and discovered that the createAdminUserIfRequired() method is called before the createSampleData() method in the Bootstrap.groovy code:
def init = { servletContext ->
environments {
development {
if (!Post.count()) createSampleData()
}
test {
if (!Post.count()) createSampleData()
}
}
// Admin user is required for all environments
createAdminUserIfRequired()
}
So it appears that the environments {} block is a configuration instruction that will get executed after any method calls that come after that block. It would be helpful if you added a comment that tips us off to the execution order of what happens inside the init block.
Thanks!
But this forum seems to be quite active:
http://www.manning-sandbox.com/forum.jspa?forumID=830
even though the first post in that forum says to submit posts to this forum.
Current :
In listing 3.15, we’re able to call User.addToTags() because Tag belongsTo
User. We’re also able to call Post.addToTags() because Tag belongsTo Post.
But Post doesn’t belongTo Tag, so we can’t call Tag.addToPosts().
The only part where "User.addToTags()" is called is in listing 3.19, it's the same for Post.addToTags(). Maybe it's in the wrong place ?
Little suggestion :
But as shown in listing 3.16, Post doesn’t belongTo Tag, so we can’t call Tag.addToPosts().
Explanation :
The important part is changing the place of the paragraph, but I think it won't hurt to reference 3.16 in order to explain the last sentence.
From : http://www.manning-sandbox.com/thread.jspa?messageID=141570𢤂
Current :
We’ll start with the simplest approach: updating the style of the pages though Cascading
Style Sheets (CSS).
Correction :
We’ll start with the simplest approach: updating the style of the pages through Cascading
Style Sheets (CSS).
From : http://www.manning-sandbox.com/thread.jspa?threadID=57346&tstart=30
The dynamic search (after figure 5.9) as written does exact match, but that may not be obvious to the reader. I think using ilike field, "%${value}%"
would make things easier to understand. At least, it would support the natural tendency to enter a part of a name, like 'rocher', to search for 'Graeme Rocher'.
Would be nice to use the features of the Database Migration plugin to cover off the setup SQL for the persistent Quartz timers
package com.grailsinaction
import grails.test.mixin.TestFor
import spock.lang.Specification
import grails.test.mixin.Mock
@TestFor(PostController)
@Mock(User)
class PostControllerSpec extends Specification {
def "Get a users timeline given their id"() {
given: "A user with posts in the db"
User chuck = new User(userId: "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 userid parameter"
params.id = chuck.userId
when: "the timeline is invoked"
def model = controller.timeline()
then: "the user is in the returned model"
model.user.userId == "chuck_norris"
model.user.posts.size() == 2
}
}
This code will produce an error when we launch grails test-app:
. The error is:
"No signature of method com.grailsinaction.User.addToPosts() is applicable for argument types : (com.grailsinaction.Post) values: [com.grailsinaction.Post : (unsaved)]"
To correct it, I had to modify the @mock statement as follows :
@Mock([User, Post])
Download code from GitHub not showing wrapper functionality yet.
It seems that the UserController.register2()
action returns a model on two of its code paths. Unfortunately, there is no register2
view, so those code paths result in 404s.
The action should either render the register
view or redirect to it. Alternatively, we add a register2
action, but that's my least preferred option.
For the test to pass, the following line needs to be changed
from: User.get(joe.id).userId == joe.userId
to: User.get(joe.id).loginId == joe.loginId
It appears an ivar was renamed.
There are several more listings in this chapter with the same problem. Some tests use User.userId and others use User.loginId.
I am new to Grails. Just followed graina2 step by step....
After creating domain class Quote.groovy and running:--
import qotd.*
def quote = Quote.findByAuthor("Chuck Norris Facts")
println quote.content
In Groovy Console, I get error as_-
No property found for name [author] for class [class qotd.Quote]
cmd window shows:--
Javassist Enhancement failed: qotd.Quote
groovy.lang.MissingPropertyException: No such property: hasProperty for class: groovy.lang.MetaClassImpl
at grails.ui.console.GrailsSwingConsole.runInstance(GrailsSwingConsole.g
roovy:61)
I am not sure, Am I missing something?
Lots of people asking about it, so mention that chapter early in chapter one.
The book says that when the following code is run as a script in the console:
Post.where { user.loginId == 12 }.list()
we should see a ClassCastException because loginId is a String property and 12 is not a String.
Actually, the integer is automatically converted to a String so no ClassCastException is thrown. I verified this by adding the following code to Bootstrap.groovy:
def twelve = new User(loginId: "121212",
password: "aaaaaa",
profile: new Profile(fullName: "Twelfth man", email: "[email protected]"),
dateCreated: now - 2).save(failOnError: true)
twelve.addToPosts(content: "Very first post")
twelve.addToPosts(content: "Second post")
twelve.save(failOnError: true)
and changing the console script to:
import com.grailsinaction.*
println Post.list().user.loginId
println Post.where { user.loginId == 121212 }.list()
println "There are ${Post.count()} posts in the database"
The script results were:
groovy> import com.grailsinaction.*
groovy> println Post.list().user.loginId
groovy> println Post.where { user.loginId == 121212 }.list()
groovy> println "There are ${Post.count()} posts in the database"
[121212, 121212, dillon, sara, phil, sara, phil, sara, phil, phil, sara, sara, sara, phil, phil]
[com.grailsinaction.Post : 8, com.grailsinaction.Post : 9]
There are 15 posts in the database
Test "Query against a range value" expects a list of user names (loginId) that are not part of what is created in BootStrap.
Assertion should read
users*.loginId == ["phil", "jeff", "graeme", "frankie", "burt", "admin"]
instead.
Under grails 2.2.3, compilation errors are still not displayed in the grails console swing result panel. Since you talk about AST transformations in "How do they work?" sidebar, I suggest you also mention the groovy AST browser (Script | Inspect Ast) where such compilation errors are spelled out explicitly and nicely.
As per page 10, If I close the application with ctrl-c and again run the application, I get the error:-
java.net.BindException: Address already in use: JVM_Bind:8080
I need to manually kill the earlier process and restart again. Is this a problem with closing the applicaion with ctrl-c
Could you emphasize that the Bootstrap.groovy file in the GitHub repository must be downloaded for the tests in this chapter to work?
I had copied this listing to create my own Bootstrap.groovy file and practiced creating my own sample data.
My approach in working through the book is to copy code from the book's listings and use the command line to ensure I am understanding every step it takes for all files to appear in the project. I'm not just copying all project files from the repository and then reading the chapter. So reminders about which repository files are required or assumed to be downloaded would be helpful. Thanks!
Hi Peter,
On page 6 I think there is typo mistake. It states:
"...you can create a custom /grails-app/conf/BuildConfig.groovy file with an entry for..."
Since BuildConfig.groovy is created by default; it shouldn't be mentioned that one should create it.
Thanks,
Rasheed
I am new to Groovy and Grails, and following the Grails In Acton 2nd Edition step-by-step to learn from scratch. Have a question on Section 1.5.1 Scaffolding: add rocket fuel
After following step-by-step, and browsing to http://localhost:8080/qotd/quote/list gives the following error (have also attached a figure)
HTTP Status 404 - /qotd/quote/list
type Status report
message /qotd/quote/list
description The requested resource is not available.
Apache Tomcat/7.0.42
However, browsing to http://localhost:8080/qotd/quote/create works fine (matches with Figure 1.12 in the book), and I am able to create a new quote. At this url, if I hover/click on the "Quote List" button it shows/takes me to http://localhost:8080/qotd/quote/index (and NOT to the list of quotes). Please see that attached figure.
The versions I am using are:
App version: 0.1
Grails version: 2.3.1
Groovy version: 2.1.8
JVM version: 1.7.0_45
Is this a bug in Grails or I am missing something?
Thank you and any help would be highly appreciated.
The code on GitHub is consequently using User.loginId but there are still a lot of code snippets using User.userId
No big deal and I assume this is something that you already planned to clean up but just so you don't miss it I put a ticket for it now.
Some of the references, there are more and easily found by search in the pdf document:
Listing 3.3
Listing 3.11
Listing 6.1 (findByUserId, GitHub code is findByLoginId)
The "A Word on Package Naming" sidebar doesn't make it clear whether the example QOTD application is using the grails.project.groupId
setting or not. For example, figure 1.8 shows the qotd
package being used rather than com.grailsinaction.qotd
.
Is the latter just an example suggestion? If so, then the sidebar should make this clear and point out that the default qotd
is used throughout the chapter and the GitHub source code. Otherwise, the examples need updating to use com.grailsinaction.qotd
.
We read:
"Grails ships with a copy of Jetty (an embeddable Java web server—there is talk that a future version will switch to Tomcat)"
Grails 2.2.3 apparently runs Tomcat by default. From the qotd welcome page:
Application Status
App version: 0.1
Grails version: 2.2.3
Groovy version: 2.0.8
JVM version: 1.7.0_25
Reloading active: true
Controllers: 2
Domains: 1
Services: 3
Tag Libraries: 13
Installed Plugins
i18n - 2.2.3
logging - 2.2.3
core - 2.2.3
urlMappings - 2.2.3
resources - 1.1.6
jquery - 1.8.3
databaseMigration - 1.3.2
webxml - 1.4.1
tomcat - 2.2.3 <---
controllers - 2.2.3
codecs - 2.2.3
domainClass - 2.2.3
converters - 2.2.3
dataSource - 2.2.3
filters - 2.2.3
groovyPages - 2.2.3
servlets - 2.2.3
mimeTypes - 2.2.3
hibernate - 2.2.3
validation - 2.2.3
scaffolding - 2.2.3
services - 2.2.3
cache - 1.0.1
Please consider adding instructions for downloading the GitHub code at the beginning of each chapter. Once you fix the Profile.groovy belonging to User problem, it works flawlessly if I replace my hubbub directory with the hubbub subdirectory for that chapter.
The big gotcha, of course, is that the code is specifically for v2.2.1. At the beginning of the book, it says to download the latest grails version. That leads to all kinds of problems. Using v.2.2.1 avoids the hassle of upgrades, googling config changes needed for jar incompatibilities, etc.
I think you should consider suggesting how to download and organize multiple versions of grails, and then swap them in and out with environment variables. Maybe not many people need that kind of advice, but the ones that do will probably really appreciate it.
We read:
"Let’s Ajax-ify our random.gsp view. First, we have to add the Ajax library to our <head> element (we’ll use Prototype ... )"
But then:
"<g:javascript library="jquery" />"
....
From this post on the Manning forums, current:
List<String> sortedPostContent = foundUser.posts.collect { it.content }.sort() #2
[...]
sortedPostContent == ['First', 'Second', 'Third'] #3
[...]
#2 Iterates through User’s posts
#3 Sorts posts alphabetically
[...]
for our test case we sort them alphabetically to make the comparison meaningful #3
Suggestion :
#2 Iterates through User’s posts and sort them alphabetically
#3 Compare retrieved values to known values
[...]
for our test case we sort them alphabetically to make the comparison meaningful #2
The "Refactoring homepage and breaking tests" on page 76 tells us to update User()
constructors to change user.homepage
with user.profile.homepage
, but there is no constructor in User class (at that point) and what needs to be done to fix tests is more than just updating the references, as we have to stop trying to assert on homepage as it is now validated with Profile
, not User
(as per the comments in UserIntegrationSpec
).
I think mentionning the effect of extracting homepage
into another instance, with respect to validate()
and thus asserts in spec tests, would be a good addition here.
The discussion related to "Listing 3.15 The Tag object models relationships to both Post and User" doesn't explain why the Tag class has a back reference to the User class. Given that back reference, it also doesn't explain why the belongsTo in Tag must use that particular form in order to support the user back reference. Consequently, while the discussion about the Post-Tag relationship is fine, the discussion of the User-Tag relationship is quite weak.
When I run grails create-*
, instead of generating *Tests.groovy
(as mentioned in the book), it generates *Spec.groovy
. Below are the examples.
Example 1: Pg. 10
grails create-controller quote
creates
| Created file test/unit/qotd/QuoteControllerSpec.groovy
instead of (as mentioned in the book)
| Created file test/unit/qotd/QuoteControllerTests.groovy
Example 2: Pg. 17
grails create-domain-class quote
creates
| Created file test/unit/qotd/QuoteSpec.groovy
instead of (as mentioned in the book)
| Created file test/unit/qotd/QuoteTests.groovy
Example 3: Pg. 25
grails create-service quote
creates
| Created file test/unit/qotd/QuoteServiceSpec.groovy
instead of
| Created file test/unit/qotd/QuoteServiceTests.groovy
Anyways, I added a method called testStaticQuoteReturnsQuicheQuote
in my QuoteServiceSpec.groovy
class as follows, and then ran grails test-app QuoteServiceSpec
and it did NOT run any tests (in the command prompt I got | Completed 0 unit test, 0 failed in 0m 1s
).
Here is my QuoteServiceSpec.groovy
class.
package qotd import grails.test.mixin.TestFor import spock.lang.Specification /** * See the API for {@link grails.test.mixin.services.ServiceUnitTestMixin} for usage instructions */ @TestFor(QuoteService) class QuoteServiceSpec extends Specification { def setup() { } def cleanup() { } void "test something"() { } void testStaticQuoteReturnsQuicheQuote(){ Quote staticQuote = service.getStaticQuote() assertEquals("Anonymous", staticQuote.author) assertEquals("Real Programmers Don't eat quiche", staticQuote.content) } }
My questions:
Thank you in advance for any suggestions.
Be good to have an Explorer view of a Grails app and arrow out the key sections of a Grails app.
The search term is not displayed in the results page because the wrong property is passed back by the controller. I got the code to work by changing
from: term: params.loginId,
to: term: params.query,
or: term: query,
When launching the app with the bootstrap file from github, I get an exception during the create admin portion.
Fresh Database. Creating ADMIN user.
| Error 2013-06-06 10:11:52,448 [localhost-startStop-1] ERROR hibernate.AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
Message: null id in com.grailsinaction.Profile entry (don't flush the Session after an exception occurs)
This is the code I have copied from the git repo:
private createAdminUserIfRequired() {
if (!User.findByLoginId("admin")) {
println "Fresh Database. Creating ADMIN user."
def profile = new Profile(email: "[email protected]")
new User(loginId: "admin", password: "secret", profile: profile).save()
}
else {
println "Existing admin user, skipping creation"
}
}
I think that sorting should be introduced in more details at end of section 3.4.2 or it should not be touched until later in the book.
Rationale
If one feels the urge to add spock tests verifying sort behavior of Post
and user.posts
, as I did after reading section 3.4.2, they're in for a bumpy ride. Here is what observed:
User.posts
is backed by a HashSet which means that calling order of user.addToPosts()
has nothing to do with the order the collection is processed by hibernate when actually inserting records to the database;user.posts
collection;Post
instance was instanciated (in the JVM), but really the instant in time when it got inserted into the database;Because of that, tirival code to check for sorting behavior does not work and writing tests that work is not easy for the intended audience (from my point of view).
If you think it is worth it to address the subject of sorting at that point, I would suggest adding the following tests to PostIntegrationSpec
with a pargraph or two discussing the details :
def "Ensure posts linked to a user are sorted by date descending, earliest first"() {
given: "A user with several posts"
User user = new User(loginId:'joe', password: 'secret')
user.addToPosts(new Post(content:'First post')).save(failOnError: true, flush: true)
user.addToPosts(new Post(content:'Second post')).save(failOnError: true, flush: true)
user.addToPosts(new Post(content:'Third post')).save(failOnError: true, flush: true)
when: "The user is saved"
user.save(failOnError: true)
then: "The posts appear sorted by date created, descending"
User.withNewSession { session ->
User.get(user.id).posts.collect { it.content } == [ 'Third post', 'Second post', 'First post' ]
}
}
def "Ensure listing post collection is using sort order"() {
given: "A user with several posts"
User user = new User(loginId: "joe", password: 'secret')
user.addToPosts(new Post(content: "B")).save(failOnError: true, flush: true)
user.addToPosts(new Post(content: "C")).save(failOnError: true, flush: true)
user.addToPosts(new Post(content: "A")).save(failOnError: true, flush: true)
user.save(failOnError: true, flush: true)
when: "the list of all posts is retrieved"
List<Post> posts = Post.list()
then: "The posts for user created are in date created order"
posts.collect { it.content } == Post.list().sort { it.dateCreated }.reverse().collect { it.content }
}
This is with
static mapping = {
posts sort: 'dateCreated', order: 'desc'
}
specified in class User
, and
static mapping = {
sort dateCreated: 'desc'
}
specified in class Post
.
At end of section 5.2.1, code snippets are using a parseDate()
method that is not defined in the text. I know there is a paragraph stating not to worry and that its actual implementation is irrelevant, but, if others are like me, they will end up running most (if not all) code snippets in a grails console and the snippets with parseDate()
will fail. Changing them is not terribly difficult, but I fail to see the benefit of having this hypothetic parseDate()
method in there.
I would suggest to use new Date()
(and variants) as it is done in some other snippets in this chapter. I prefer to have code that I can copy/paste in my grails console to experiment as I learn rather than have code that looks like production code (if that was the intent?).
Same thing for sub-section "5.3.2 Introducing Criteria queries" where the first fragment use a parseDate(params.fromDate)
while a ge "dateCreated", new Date() -1
would do the job, like what is done in the second fragment in that sub-section 5.3.2.
The code fragment of section 5.3.2 (the withCriteria
) uses between 'created'
but should be between 'dateCreated'
.
Listing is as follows:
Listing 3.3 Saving and retrieving a domain object from the database
package com.grailsinaction
import spock.lang.*
import grails.plugin.spock.*
class UserIntegrationSpec extends IntegrationSpec { #1
def "Saving our first user to the database"() {
given: "A brand new user"
def joe = new User(loginId: 'joe', password: 'secret',
homepage: 'http://www.grailsinaction.com')
when: "the user is saved" joe.save() #2
then: "it saved successfully and can be found in the database"
joe.errors.errorCount == 0 #3
joe.id != null #4
User.get(joe.id).userId == joe.userId #5
}
}
Last line should be:
User.get(joe.id).loginId == joe.loginId #5
test-app output before and after the above correction:
| Compiling 1 source files....
| Compiling 1 source files.....
| Running 1 spock test... 1 of 1
| Failure: Saving our first user to the database(com.grailsinaction.UserIntegrationSpec)
| groovy.lang.MissingPropertyException: No such property: userId for class: com.grailsinaction.User
at com.grailsinaction.UserIntegrationSpec.Saving our first user to the database(UserIntegrationSpec.groovy:19)
| Completed 1 spock test, 1 failed in 602ms
| Tests FAILED - view reports in C:\netscripts\smcccd\hubbub\target\test-reports
| Compiling 1 source files.....
| Compiling 1 source files.....
| Completed 1 spock test, 0 failed in 378ms
| Tests PASSED - view reports in C:\netscripts\smcccd\hubbub\target\test-reports
Hi Peter,
If I am not wrong then now Grails (2.2.1) by default comes with tomcat and not Jetty.
In section 1.2 and page 4 it states:
"Grails ships with a copy of Jetty (an embeddable Java web server—there is talk that a future version will switch to Tomcat), which Grails uses to host your application during the
development and testing lifecycle."
Please correct it if you agree.
Regards,
Rasheed
When using def users = User.list(sort: 'loginId', order: 'asc', max: 5, fetch: [posts: 'eager'])
the result list has 3 items whereas def users = User.list(sort: 'loginId', order: 'asc', max: 5)
has 5.
I wonder if this is a bug in grails (version 2.2.3) ?
Here is a spec that should pass but where the tests using the fetch
parameter are failing:
package com.grailsinaction
import grails.plugin.spock.IntegrationSpec
/**
* Integration tests that should pass, but the one specifying fetch parameter to list() fails.
*/
class UserIntegrationCh531Spec extends IntegrationSpec {
def setup() {
}
def cleanup() {
}
def "Static User list with sorting order"() {
given: "User domain static list() method is used with parameters sort and order"
def users = User.list(sort: 'id', order: 'asc')
when: "a copy of the list is sorted by id"
def copy = users.collect().sort { it.id }
then: "both are equals"
users == copy
}
def "Static User list with sorting order and eager fetch"() {
given: "User domain static list() method is used with parameters sort, order and fetch"
def users = User.list(sort: 'id', order: 'asc', fetch: [posts: 'eager'])
when: "a copy of the list is sorted by id"
def copy = users.collect().sort { it.id }
then: "both are equals"
users == copy
}
def "Static User list with max"() {
given: "User domain static list() method used with a max parameter"
def max = 4
def users = User.list(max: max)
when: "We count the results"
def count = users.size()
then: "it equals the max given"
count == max
}
def "Static User list with max and eager fetch"() {
given: "User domain static list() method used with a max parameter and fetch"
def max = 5
def users = User.list(max: max, fetch: [posts: 'eager'])
when: "We count the results"
def count = users.size()
then: "it equals the max given"
count == max
}
}
The assertion (#5) is false as the sum of 3 and 7 is 10. If this is intentional, there should be a paragraph talking about seeing the test become red when run and how to fix it.
However, I think the first Spock test introduced should pass, unless you want to go into a discussion on the merit of writing tests that fails first, then make them run; but then, the test should fail not because of a error in the test but because of the code under test.
Are you assuming the readers of your book will know how to create the search form all on their own? What to name it, where to save it, and what the content of said file should be? All based on Figure 5.9?
If so, you should say so. And I think you maybe giving some of us more credit then we are due.
At the very least, you should point the reader to the Chapter 5 download files and AdvSearch.gsp (for those of us who expected a little more instruction on how to "generate a basic search form for Hubbub profiles". I am assuming this is what I am supposed to do.
And then where are we supposed to put the "advResults()" code that follow figure 5.9? I am still searching the download files for that code.
Did someone else write this section? It does not seem to be on par with the first 4 chapters.
Well I found the "AdvResults" code in UserController.groovy file. I still think this section needs some more work to be user friendly and instructional.
LameSecurityFilters
only redirects to the login form on addPost
and deletePost
actions (I'm not sure the latter one even exists). So if the user isn't logged in, addPostAjax
just throws an NPE.
Also problematic is the lack of a login form 😉
A potential solution for addPostAjax
is for it to return a 401 if there is no 'user' object in the session. In fact, we should probably display a login link and hide the post submission form until there is a user in the session.
After making all the Hibernate logging changes in section 5.2.2, I noticed that only the last 600 lines that Hibernate logged showed up in the console output. I cannot see what Hibernate logged in the first few hundred lines. How can I configure the console to retain all/more lines so I can spot problems earlier in scripts? Thanks.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.