Go To Homepage



Book Details
Beginning Groovy and Grails: From Novice to Professional book cover
  • By Christopher M. Judd Joseph Faisal Nusairat Jim Shingler
  • ISBN13: 978-1-4302-1045-0
  • ISBN10: 1-4302-1045-1
  • 440 pp.
  • Published Jun 2008
  • Print Book Price: $42.99
  • eBook Price: $30.09



Errata Submission

If you think that you've found an error in Beginning Groovy and Grails: From Novice to Professional, please let us know about it. You will find any confirmed erratum below, so you can check if your concern has already been addressed.

Submit Errata
Beginning Groovy and Grails: From Novice to Professional (978-1-4302-1045-0)

Errata

Issue Author's Response
Chapter 5 p. 123
Listing 5.11

I have seen the response on the page to this issue, but I have the code copied exactly from the source and have checked it several times. I still get the following error:

Error 500: Error processing GroovyPageView: Error executing tag <g:form>: org.codehaus.groovy.grails.web.pages.exceptions.GroovyPagesException: Error evaluating expression [User.list()] on line [22]: java.lang.NullPointerException: Cannot invoke method list() on null object
Servlet: grails
URI: /collab-todo/grails/user/login.dispatch
Exception Message: Cannot invoke method list() on null object
Caused by: Error processing GroovyPageView: Error executing tag <g:form>: org.codehaus.groovy.grails.web.pages.exceptions.GroovyPagesException: Error evaluating expression [User.list()] on line [22]: java.lang.NullPointerException: Cannot invoke method list() on null object
Class: /WEB-INF/grails-app/views/user/login.gsp
At Line: [-1]
Code Snippet:
We think you might have run into this defect: http://jira.codehaus.org/browse/GRAILS-4842
Chapter 4, 122- 126
grails version 1.1.1

I made the changes as recommend in this Errata for page 123 and 125 as suggested by the author. Everything seems to work well, however when I login a "< /b> " is appended at the end of the user name after login.
I have searched the book and I could not find a </b> reference in the book. The errata appear to have matching <b> so I don't that would cause any issues. The only thing I can suggest is to make sure it was not accidentally duplicated or a tool did not add an extra one. You might also check the message tag as that is the only other place I could imagine it being. The final thought is to make sure you are using a browser that does not have a <b> bug but I can't believe that would be the case.
Since Grails 1.1 does not create an integration test for domain classes automatically, I created explicitely an integration test with Grails 1.1.2 by calling "grails create-integration-test <name>" for e.g. the "todo" Domain class from chapter 4

(side note:
amazing: Grails 1.1.2 generates the integration test and extends GrailsUnitTestCase - I changed this manually to GroovyTestCase but the error is the same)

I tried to implement the related tests from chapter 4.

class TodoITestTests extends GroovyTestCase {
protected void setUp() {
Todo.list()*.delete() ...


I get the following error:
==========================
No signature of method: static Todo.list() is applicable for argument types: () values: []
groovy.lang.MissingMethodException: No signature of method: static Todo.list() is applicable for argument types: () values: []
at TodoITests.setUp(TodoITests.groovy:5)
at _GrailsTest_groovy$_run_closure4.doCall(_GrailsTest_groovy:203)
at _GrailsTest_groovy$_run_closure4.call(_GrailsTest_groovy)
at _GrailsTest_groovy$_run_closure2.doCall(_GrailsTest_groovy:147)
at _GrailsTest_groovy$_run_closure1_closure19.doCall(_GrailsTest_groovy:113)
at _GrailsTest_groovy$_run_closure1.doCall(_GrailsTest_groovy:96)
at TestApp$_run_closure1.doCall(TestApp:66)
at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:344)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:334)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:495)
at gant.Gant.processTargets(Gant.groovy:480)

Testcase: testToString took 0,009 sec
Caused an ERROR
No signature of method: static Todo.list() is applicable for argument types: () values: []
groovy.lang.MissingMethodException: No signature of method: static Todo.list() is applicable for argument types: () values: []
at TodoITests.setUp(TodoITests.groovy:5)
at _GrailsTest_groovy$_run_closure4.doCall(_GrailsTest_groovy:203)
at _GrailsTest_groovy$_run_closure4.call(_GrailsTest_groovy)
at _GrailsTest_groovy$_run_closure2.doCall(_GrailsTest_groovy:147)
at _GrailsTest_groovy$_run_closure1_closure19.doCall(_GrailsTest_groovy:113)
at _GrailsTest_groovy$_run_closure1.doCall(_GrailsTest_groovy:96)
at TestApp$_run_closure1.doCall(TestApp:66)
at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:344)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:334)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:495)
at gant.Gant.processTargets(Gant.groovy:480)
I just attempted what you described with the simplest of examples. The test pass for me as an integration test. I am also using Grails 1.1.2 but Java 1.5 on Mac.

One thing I would check that commonly happens is if you try to create a unit test first that fails and then realized it had to be an integration test, you may have created an integration test with the same name so it may be actually failing on the original unit test. You may also want to run a grails clean to make sure any classes are not lying around.
I am trying to run the collab-todo applcation I get this message:
Application expects grails version [1.0.3], but GRAILS_HOME is version [1.1.2] - use the correct Grails version or run 'grails upgrade' if this Grails version is newer than the version your application expects.

I tried to run 'grails upgrade', but got this error at the end:
You currently already have a version of the plugin installed [hibernate-1.1.2]. Do you want to upgrade this version? (y, n)
Installing plug-in hibernate-1.1.2
y
[delete] Deleting directory C:\Documents and Settings\Bahram\.grails\1.1.2\projects\collab-todo\plugins\hibernate-1.1.2
: Unable to delete file C:\Documents and Settings\Bahram\.grails\1.1.2\projects\collab-todo\plugins\hibernate-1.1.2\lib\dom4j-1.6.1.jar
at org.apache.tools.ant.taskdefs.Delete.handle(Delete.java:620)
at org.apache.tools.ant.taskdefs.Delete.removeDir(Delete.java:679)
at org.apache.tools.ant.taskdefs.Delete.removeDir(Delete.java:675)
at org.apache.tools.ant.taskdefs.Delete.execute(Delete.java:543)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:105)
at org.apache.tools.ant.Task.perform(Task.java:348)
at _PluginDependencies_groovy$_run_closure26.doCall(_PluginDependencies_groovy:871)
at _PluginDependencies_groovy$_run_closure26.call(_PluginDependencies_groovy)
at _GrailsPlugins_groovy.completePluginInstall(_GrailsPlugins_groovy:80)
at _GrailsPlugins_groovy.this$4$completePluginInstall(_GrailsPlugins_groovy)
at _GrailsPlugins_groovy$this$4$completePluginInstall.callCurrent(Unknown Source)
at _GrailsPlugins_groovy.withPluginInstall(_GrailsPlugins_groovy:68)
at _GrailsPlugins_groovy.this$4$withPluginInstall(_GrailsPlugins_groovy)
at _GrailsPlugins_groovy$_run_closure3.doCall(_GrailsPlugins_groovy:54)
at _GrailsPlugins_groovy$_run_closure3.call(_GrailsPlugins_groovy)
at _GrailsPlugins_groovy$_run_closure6.doCall(_GrailsPlugins_groovy:133)
at Upgrade$_run_closure1.doCall(Upgrade.groovy:242)
at Upgrade$_run_closure2.doCall(Upgrade.groovy:250)
at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:344)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:334)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:495)
at gant.Gant.processTargets(Gant.groovy:480)
Error installing plugin: Unable to delete file C:\Documents and Settings\Bahram\.grails\1.1.2\projects\collab-todo\plugins\hibernate-1.1.2\lib\dom4j-1.6.1.jar: Unable to delete file C:\Documents and Settings\Bahram\.grails\1.1.2\projects\collab-todo\plugins\hibernate-1.1.2\lib\dom4j-1.6.1.jar
On the Mac with Java 1.5 using Grails 1.1.2, I did not receive such an error. I would try manually deleting C:\Documents and Settings\Bahram\.grails\1.1.2\projects\collab-todo\plugins\hibernate-1.1.2 and possibly even C:\Documents and Settings\Bahram\.grails\1.1.2\projects\collab-todo\ and see if that helps. Also a "grails clean" may help.
No unit test works! e.g TodoTests.groovy:

groovy.lang.MissingMethodException: No signature of method: static Todo.list() is applicable for argument types: () values: []
at groovy.lang.MetaClassImpl.invokeStaticMissingMethod(MetaClassImpl.java:1364)
at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1350)
at org.codehaus.groovy.runtime.callsite.StaticMetaClassSite.call(StaticMetaClassSite.java:35)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:121)
at TodoTests.setUp(TodoTests.groovy:5)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90)


It look like that no db-methods are created???

So what can I do????
That is the error you will get if you run the test from the unit test directory rather than the integration test directory. Since the default in Grails 1.1 is now to create a unit test rather than an integration test, insure you don't have the same test name in both the integration test and unit test directories making the reports impossible to read since it will overwrite one of the tests.
Error listed below your answer is truly appreciated.
grails version:1.1.1
Book Page: 123
Listing: 5-11

*****************************************************
Error 500: Error processing GroovyPageView: Error executing tag <g:form>: org.codehaus.groovy.grails.web.pages.exceptions.GroovyPagesException: Error evaluating expression [User.list()] on line [22]: java.lang.NullPointerException: Cannot invoke method list() on null object
Servlet: grails
URI: /collab-todo/grails/user/login.dispatch
Exception Message: Cannot invoke method list() on null object
Caused by: Error processing GroovyPageView: Error executing tag <g:form>: org.codehaus.groovy.grails.web.pages.exceptions.GroovyPagesException: Error evaluating expression [User.list()] on line [22]: java.lang.NullPointerException: Cannot invoke method list() on null object
Class: /WEB-INF/grails-app/views/user/login.gsp
At Line: [-1]
Code Snippet:
*****************************************************

Thank you
* Page 123 - Due to a bug fix in Grails 1.0.3 the actionSubmit on line 26 of Listing 5-11 should be the following:

<span class="button"><g:actionSubmit value="Login" action="handleLogin"/>
* Page 125 - Due to a bug fix in Grails 1.0.3 the handleLogin action in Listing 5-13 does not work any longer. The new implementation is:

def handleLogin = {
def user = User.findByUserName(params.userName)
if (!user) {
flash.message = "User not found for userName: ${params.userName}"
redirect(action:'login')
return
} else {
session.user = user
redirect(controller:'todo')
}
}
On page 123, Listing 5-11. The Login View (login.gsp)
Even when coppied straight from the source I keep getting the error

Error 500: Error processing GroovyPageView: Error executing tag <g:form>: org.codehaus.groovy.grails.web.pages.exceptions.GroovyPagesException: Error evaluating expression [User.list()] on line [21]: java.lang.NullPointerException: Cannot invoke method list() on null object
* Page 123 - Due to a bug fix in Grails 1.0.3 the actionSubmit on line 26 of Listing 5-11 should be the following:

<span class="button"><g:actionSubmit value="Login" action="handleLogin"/>
In chapter 4 page 81, when you create a Domain Class you describe (in a note) that Grails will create an integration test. But when I testet this my version (1.1.1) keeps creating unit tests.

Were there any changes in Grails? Or am I doing somthing wrong?
No you are right there were changes in i believe 1.1 that it now creates unit tests by default, you can manually create your own integration test now.

FWIW testing has changed a bit since the book was released with more support for Controller, TagLib, and Service base classes for testing.
Chapter 5. Page 151. Filter for user actions.

The statement redirect(action:'list'), caused problems for me ... errors returned about not finding list action for Filter.

I changed it to redirect(controller:'user',action:'list') and it then worked as I expected.
I believe this was a change in more recent versions of Grails. Thanks for reporting it.
Chapter 6, page 212. The paragraph states:
Luckily, as you may have guessed, GORM has a way around this. If you have lastUpdated and dateCreated named properties, you can configure GORM to automatically update them by adding the following line in the mapping:
autoTimestamp false

However, the Grails GORM Events page (http://www.grails.org/GORM+-+Events) states the opposite:
By merely defining a lastUpdated and dateCreated property these will be automatically updated for you by GORM. If this is NOT the behaviour you want you can disable this feature with:
autoTimestamp false
Yes this is a mistake. That has been reported at http://www.beginninggroovyandgrails.com/site/content/errata and I think was fixed in the second printing.
The keyword "def" is used starting on page 7 and then throughout the book. However, I can't find anywhere in the book that explains what it means. I think it is a significant issue that should be remedied in the next edition.

For example, what is the difference between:
a = "bar"
String a = "bar"
def a = "bar"

I found the answer online here:
http://groovy.codehaus.org/Scoping+and+the+Semantics+of+%22def%22
Good recommendation.
On page 98, when you create de domain class Category and its controller, it seems to be an issue with an abstract interface named groovy.lang.Category within Grails 1.1.1.

This is the error:

"2009-06-29 10:14:48,645 [main] ERROR plugins.DefaultGrailsPlugin - Cannot generate controller logic for scaffolded class interface groovy.lang.Category. It is not a domain class!"

I renamed the class to 'Categ' and that solved the problem.

My questions are, is that a problem in that version of Grails?, Is there a way to fix this other than changing the class name?, Are class name restrictions in Grails?

Thanks
As of Grails 1.1 the Category domain class collides with with a new groovy.lang.Category. To make the application work you have three choices rename the Category to something else like CategoryItem, put Category into a package or rename it Group which would require a mapping since Group is a reserved word in SQL.

It is really more of a Java namespace issue than a Grails or Groovy issue. Since the Category class was introduced in Groovy 1.6 into the groovy.lang package which is implicitly imported in Groovy causing the ambiguity and in this case the JVM class loader will find groovy.lang.Category first. The concept of Java packages was created to prevent this. This points out putting domain classes into a package should be considered a best practice.
If you try to work the examples from chapter 2, page 13, Listing 2-5 and having installed Groovy 1.6 even if you change the code to point to groovy-all-1.6.2.jar you get a ClassNotFoundException from java. If the ClassNotFoundException you are getting is regarding groovy.lang.Script, check to make sure your GROOVY_HOME is set to the same version number of the jar you are using. Having that miss match is the only way I could get a ClassNotFound exception and I tried 4 different versions of Groovy.

Exception in thread "main" java.lang.NoClassDefFoundError: groovy/lang/Script
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$000(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
Caused by: java.lang.ClassNotFoundException: groovy.lang.Script
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
... 12 more
Per page 81-82, when I create a todo domain class, a integration test should also be created. However, in my set a unit test is automatically created, but not a integration test. No big deal I think. I can create a integration test manually in the tests/integration folder. I did. I copied the code in page 83 to tests/integration/TodoTests.groovy and ran grails test-app. Both my tests failed. Per the book, the testPersist should have succeeded. Looking in to the test report, I noticed that both the tests failed because of
"No signature of method: static Todo.list() is applicable for argument types: () values: []"

the Todo.list()*.delete in setUp() is not working. Per my understanding since the test is in integration folder, the dynamic methods should have been generated, but seems like it was not.

what went wrong?

I'm using grails-1.1.1
That is the error you will get if you run the test from the unit test directory rather than the integration test directory. Since the default in Grails 1.1 is now to create a unit test rather than an integration test, insure you don't have the same test name in both the integration test and unit test directories making the reports impossible to read since it will overwrite one of the tests. If you don't have duplicates and the problem consists, please email the project to authors@beginninggroovyandgrails.com.
Table 5-8, page 121.

richTextEditor no longer exists

http://jira.codehaus.org/browse/GRAILS-1135
Your right g:richTextEditor has been replaced by plug-ins such as FCKEditor (http://www.grails.org/plugin/fckeditor), RichUI (http://www.grails.org/plugin/richui) and Grails UI (http://www.grails.org/plugin/grails-ui).
Page 130.
I've created the integration test for UserController but when I run the integration test, I encountered this error:

No signature of method: UserController.handleLogin() is applicable for argument types: () values: []

for both UserControllerTests:testHandleLogin and UserControllerTests:testHandleLoginInvalidUser.

I've tried to debug for quite some time but have no idea why it is still not working. I've also verified that the test file resides in Integration Test and not Unit Test.

Thanks.
* Page 128 Listing 5-15, the redirect changed in Grails 1.1 the following should resolve the issue.

def handleLogin = {
def user = User.findByUserName(params.userName)
if (!user) {
flash.message = "User not found for userName: ${params.userName}"
redirect(controller: 'user', action:'login')
return
} else {
session.user = user
redirect(controller:'todo')
}
}

def logout = { if(session.user) { session.user = null redirect(controller: 'user', action:'login') } }
Page 128. Implemented the UserControllerTests in listing 5-15. I also used the correction in the Errata so the handleLogin action in UserController should be correct. I am getting the following error in both testHandleLoginInvalidUser() and testLogout()

testHandleLoginInvalidUser Error Unable to create URL for mapping [/(*)/(*)?/(*)?] and parameters [{action=login}]. Parameter [controller] is required, but was not specified!

org.codehaus.groovy.grails.web.mapping.exceptions.UrlMappingException: Unable to create URL for mapping [/(*)/(*)?/(*)?] and parameters [{action=login}]. Parameter [controller] is required, but was not specified!
at UserController$_closure2.doCall(UserController.groovy:10)
at UserController$_closure2.doCall(UserController.groovy)
at UserControllerTests.testHandleLoginInvalidUser(UserControllerTests.groovy:51)
at _GrailsTest_groovy$_run_closure4.doCall(_GrailsTest_groovy:202)
at _GrailsTest_groovy$_run_closure2.doCall(_GrailsTest_groovy:146)
at _GrailsTest_groovy$_run_closure1_closure19.doCall(_GrailsTest_groovy:112)
at _GrailsTest_groovy$_run_closure1.doCall(_GrailsTest_groovy:95)
at TestApp$_run_closure1.doCall(TestApp.groovy:66)
at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:344)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:334)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:495)
at gant.Gant.processTargets(Gant.groovy:480)
The error is related to changes with Grails 1.1. The problem is the redirect.

In UserController.groovy, . . . add controller: 'user' as a parameter to the redirects for handleLoing() and logout() that will resolve the issue.

def handleLogin = {
def user = User.findByUserName(params.userName)
if (!user) {
flash.message = "User not found for userName: ${params.userName}"
redirect(controller: 'user', action:'login')
return
} else {
session.user = user
redirect(controller:'todo')
}
}

def logout = {
if(session.user) {
session.user = null
redirect(controller: 'user', action:'login')
}
}
Page 88

after fleshing out todo domain class and running grails test-app both tests still fail

Testcase: testPersist took 0.047 sec
Caused an ERROR
No signature of method: static Todo.list() is applicable for argument types: () values: []
groovy.lang.MissingMethodException: No signature of method: static Todo.list() is applicable for argument types: () values: []
at TodoTests.setUp(TodoTests.groovy:5)
That sounds like it might have been created as a unit test rather than an integration test.
page 81

for some reason when i run the command grails create-domain-class todo it goes into the unit folder and not the integration folder

using grails 1.1
This is part of a change to Grails. Before 1.1 it would go into the integration folder as well. Now by default it is created in the unit test folder. You can still add tests to the integration folder if you want to do integration tests.
The Todo application does not work with Grails 1.1 as the Category domain collides with groovy.lang.Category. To make the application work call the Category domain object something else like CategoryItem Another alternative is putting the Category class in a package or renaming it to Group which would also require a mapping as Group is a reserved word in SQL.
chapter 9, pages 300/302 you have code to pull the domain class name from the params collection (under the name controller), but controller has already been set to "rest" by the URL mapping by the time the interceptor gets it, so this will never work, you need to rename $controller in the url mapping to something like $domain and use that to pull the domain class name instead of params.controller (which would always equal "rest") in the example code Grails 1.0.3 made a change that causes the requested URL Mapping parameters to get added to the params lists even if the controller or other URL Mapping parameter is over written. There for line of one listing 9-5 on page 300 should be:

"/$rest/$domain/$id?"{
And the second line of code on page 302 in listing 301 should be:
domainClassName = capitalize(params.domain)
code listing 5-21 contains:

<div id="menu">
<nobr>
<g:if test="${session.user}">
<b>${session.user?.firstName} ${session.user?.lastName}</b> |
<g:link controller="logout"><g:message code="topbar.logout" /></g:link>
</g:if>
<g:else>
<g:link controller="login" action="auth">
<g:message code="topbar.login" /></g:link>
</g:else>
</nobr>
</div>

But should contain:

<div id="menu">
<nobr>
<g:if test="${session.user}">
<b>${session.user?.firstName}&nbsp;${session.user?.lastName}</b> |
<g:link controller="user" action="logout"><g:message code="topbar.logout" /></g:link>
</g:if>
<g:else>
<g:link controller="user" action="login"><g:message code="topbar.login" /></g:link>
</g:else>
</nobr>
</div>
Listing 5-21 should be:

<div id="menu">
<nobr>
<g:if test="${session.user}">
<b>${session.user?.firstName}&nbsp;${session.user?.lastName}</b> |
<g:link controller="user" action="logout"><g:message code="topbar.logout" /></g:link>
</g:if>
<g:else>
<g:link controller="user" action="login"><g:message code="topbar.login" /></g:link>
</g:else>
</nobr>
</div>
I am reading on books24x7.com... so I don't have page numbers. But its in the canoo webtest section of chapter 5. After fixing the improper creation of a user error, the test then fails on the final step of verifying that the user is deleted by searching for user deleted text. This can be resolved by actually generating the scaffold as is done in the next chapter. I reproduced this error in both Windows and Linux. I suppose this is a bug in Grails 1.04, and not the book. But, it would be nice to let users know. Depending upon the stage of development when running the webtest, . . . you may be encountering a permission problem.

In Listing 5-30 a User check was added. This is the reason the test fails.
Chapter 5, Page 114-115, Listing 5-9

The rendering of the topbar template misses the div tag around it.

It should be,

<div id="topbar">
<g:render template="/common/topbar" />
</div>
You are correct. Thanks.
Listing 6-39 on pg. 204. Looks like an accidental editing error.

It seems the intent is to loop through all the elements of the params map, and append to hqlQuery on each loop. However, the loop header is missing. Note also the extra "}" before the return statement.
I've relooked at this code a few times again, and granted i just got back from a 10hr flight but I don't see any errors in it.

The loop header is there "params.each { " ... marks the beginning of the header of the loop.

The only "error" is the formatting. The } after the final hqlQuery += goes with the start of that params.each. The second one matches up to the if statement.

Take a look at TodoTests.testFindingTodosWithHQLWithSubProperties

where this method is used. If you have any other questions you can ask as well. We have a site you can monitor other errata and email all of us at -

http://www.beginninggroovyandgrails.com/
On page 124, it describes logging a user into the collab-todo application.

for some reason this does not log in. I have checked the code again and again...

any ideas?
* Page 123 - Due to a bug fix in Grails 1.0.3 the actionSubmit on line 26 of Listing 5-11 should be the following:

<span class="button"><g:actionSubmit value="Login" action="handleLogin"/>

* Page 125 - Due to a bug fix in Grails 1.0.3 the handleLogin action in Listing 5-13 does not work any longer. The new implementation is:

def handleLogin = {
def user = User.findByUserName(params.userName)
if (!user) {
flash.message = "User not found for userName: ${params.userName}"
redirect(action:'login')
return
} else {
session.user = user
redirect(controller:'todo')
}
}
Chapter 6, page 199, listing 6-30: the description of the Todo.findAllByPriorityOrStatus("2","4") should read 'in the second, you're separating the retrieval with an Or, so that if a record has a PRIORITY of "2" or a status of "4".' The submitter provided a solution, and yes that is correct that it should be a priority of 2 or a status of 4.

Thank you.

And i have also updated
http://www.beginninggroovyandgrails.com/ with the errata provided
page 181.

tablePerHierachy true -> should be false.

The default is true it will create only one table with the differentiator field.

grails 1.0.3.
Your right. It should be false.
Page 2 - Under Groovy Installation. It maybe unclear but there are platform specific installers for Windows, Ubuntu and Debian. .
Chapter 2, Listing 2-35, page 41. This line doesn't work:

list.collect { println it.printFullName() }

because you are printing the result of calling printFullName(), which itself already calls println, the result of which is null.

Instead, you should have in the User class:

def fullName() { "$firstName $lastName" }

and

list.each { println it.fullName() }
You are correct and thanks for the solution.
p266:

dueDate (should be file or associatedFile) and asociatedFile (should be associatedFile)

<tr class='prop'>
<td valign='top' class='name'><label for='dueDate'>File:</label></td>

<td valign='top'
class='value ${hasErrors(bean:todo,field:'asociatedFile','errors')}'>
<input type="file" name="asociatedFile" />
</td>
</tr>
Your correct.
on chapter 2 the java code in listing 2-7 (SayHello.java) is wrong.

void main (String [] args)

rather than void main (String args[])
Actually depending on the versions of javac either one will work. But you bring up a great point your suggest is the preferred method and will work more universally.
Around page 100 - 129

The log in doesn't work. It seems like the method handleLogin doesn't really receieve anything from the form at all. Also, if you test it by using test-app, the testHandleInvalidUser will fail with this error
"Cannot send redirect - response is already committed"
* Page 123 - Due to a bug fix in Grails 1.0.3 the actionSubmit on line 26 of Listing 5-11 should be the following:

<span class="button"><g:actionSubmit value="Login" action="handleLogin"/>

* Page 125 - Due to a bug fix in Grails 1.0.3 the handleLogin action in Listing 5-13 does not work any longer. The new implementation is:

def handleLogin = {
def user = User.findByUserName(params.userName)
if (!user) {
flash.message = "User not found for userName: ${params.userName}"
redirect(action:'login')
return
} else {
session.user = user
redirect(controller:'todo')
}
}
I bought this book online and was wondering if there is any sample code for this book yet? Yes. The sample code can be found http://www.apress.com/book/downloadfile/4089 or the most up to date version which includes fixes for Grails 1.0.3 can be found at http://www.beginninggroovyandgrails.com/site/content/source.
Chapter 5, page 156

"Additionally, you should apply the enhancement you made to the category controller and view to the to-do controller and views."

The Todo functionality also needs restriction to ensure that a user's Todos are only classified using the same user's Categories. The population of the Category dropdown on the Todo create and edit screens needs modification.
Your correct, the list of categories should be filtered for that user.
Chapter 5, page 154, listing 5-35 (Restricting the list Action)

Putting [ categoryList: Category.findAllByUser(user, params) ] in the controller doesn't address the fact that the view still contains
<div class="paginateButtons">
<g:paginate total="${Category.count()}" />
</div>

I suggest returning this map from the Action:
[ categoryList: Category.findAllByUser(user, params),
categoryCount: Category.countByUser(user) ]

and then using <g:paginate total="${categoryCount}" /> in the view.

The same will apply to the Todo list.
Good suggestion.
Chapter 5, page 149, listing 5-30:

Your check for whether or not the User being edited is the same as the logged-in user:

if (session.user.id != params.id) {

is trying to compare a Long with a String.

Replacing session.user.id with "${session.user.id}" works. (Feel free to choose a more elegant solution!)
There have been several reports of issues with the if statement. As written, it assumes that the user is logged in. There is also a type conversion issue in comparing session.user.id (Long) to params.id (String). You may want to update the code as follows:

if (session.user?.id as String != params.id) {

Notice the ? Null safe dereference and casting to String for comparison.