You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by as...@apache.org on 2026/05/01 23:15:45 UTC
(grails-website) branch asf-site-production updated: Updating apache/grails-website asf-site-production branch for GitHub Actions run:25236952628
This is an automated email from the ASF dual-hosted git repository.
asf-ci-deploy pushed a commit to branch asf-site-production
in repository https://gitbox.apache.org/repos/asf/grails-website.git
The following commit(s) were added to refs/heads/asf-site-production by this push:
new b045066f439 Updating apache/grails-website asf-site-production branch for GitHub Actions run:25236952628
b045066f439 is described below
commit b045066f4394c7e344705321ac1b2a2b5c1401a9
Author: jamesfredley <ja...@users.noreply.github.com>
AuthorDate: Fri May 1 23:15:30 2026 +0000
Updating apache/grails-website asf-site-production branch for GitHub Actions run:25236952628
---
.../adding-commit-info/3/guide/configuration.html | 47 +-
guides/adding-commit-info/3/guide/index.html | 62 +-
.../adding-commit-info/3/guide/pages/actuator.html | 2 +-
.../guide/pages/configuringGitInfoContributor.html | 39 +-
.../3/guide/pages/gradlePlugin.html | 13 +-
.../3/guide/pages/perEnvironmentConfig.html | 8 +-
guides/adding-commit-info/3/guide/single.html | 62 +-
.../adding-commit-info/3/guide/writingTheApp.html | 15 +-
.../adding-commit-info/4/guide/configuration.html | 59 +-
guides/adding-commit-info/4/guide/index.html | 74 +-
.../adding-commit-info/4/guide/pages/actuator.html | 2 +-
.../guide/pages/configuringGitInfoContributor.html | 51 +-
.../4/guide/pages/gradlePlugin.html | 13 +-
.../4/guide/pages/perEnvironmentConfig.html | 8 +-
guides/adding-commit-info/4/guide/single.html | 74 +-
.../adding-commit-info/4/guide/writingTheApp.html | 15 +-
guides/building-a-react-app/4/guide/index.html | 290 ++++-
.../4/guide/pages/customizingApi.html | 20 +-
.../building-a-react-app/4/guide/pages/post.html | 95 +-
.../4/guide/pages/rendering.html | 39 +-
.../4/guide/pages/vehicles.html | 46 +-
.../4/guide/runningTheApp.html | 20 +-
guides/building-a-react-app/4/guide/single.html | 290 ++++-
.../4/guide/writingReactComponents.html | 180 +++-
.../4/guide/writingTheApp.html | 90 +-
guides/building-a-vue-app/4/guide/index.html | 440 +++++++-
.../4/guide/pages/customizingApi.html | 20 +-
.../4/guide/pages/displayComponents.html | 85 +-
.../building-a-vue-app/4/guide/pages/headers.html | 37 +-
.../4/guide/pages/oneMoreComponent.html | 106 +-
.../4/guide/pages/postComponents.html | 59 +-
.../4/guide/pages/puttingItAllTogether.html | 16 +-
guides/building-a-vue-app/4/guide/single.html | 440 +++++++-
.../4/guide/writingTheClient.html | 303 +++++-
.../4/guide/writingTheServer.html | 137 ++-
guides/building-a-vue-app/4/img/complete.png | Bin 0 -> 288797 bytes
guides/building-a-vue-app/4/img/vueApp.png | Bin 0 -> 6244 bytes
guides/building-a-vue-app/4/img/vueConfig.png | Bin 0 -> 6179 bytes
guides/building-a-vue-app/4/img/vueDirListing.png | Bin 0 -> 25590 bytes
guides/building-a-vue-app/4/img/vueSelect.png | Bin 0 -> 35570 bytes
guides/building-a-vue-app/4/img/vueTest.png | Bin 0 -> 3228 bytes
.../4/guide/index.html | 240 ++++-
.../4/guide/pages/domainClass.html | 17 +-
.../4/guide/pages/domainClassTest.html | 42 +-
.../4/guide/pages/v1Controller.html | 16 +-
.../4/guide/pages/v1FunctionalTests.html | 40 +-
.../4/guide/pages/v2Controller.html | 86 +-
.../4/guide/pages/v2FunctionalTests.html | 39 +-
.../4/guide/single.html | 240 ++++-
.../4/guide/version2.html | 125 ++-
.../4/guide/writingTheGrailsApp.html | 115 +-
.../4/img/android-announcements-overview.png | Bin 0 -> 624057 bytes
.../4/img/android-studio-newproject1.png | Bin 0 -> 103166 bytes
.../4/img/android-studio-newproject2.png | Bin 0 -> 129522 bytes
.../4/img/android-studio-newproject3.png | Bin 0 -> 99753 bytes
.../4/img/android-studio-newproject4.png | Bin 0 -> 129522 bytes
.../4/img/extras.png | Bin 0 -> 456770 bytes
.../4/img/overview.png | Bin 0 -> 581170 bytes
.../4/img/version2overview.png | Bin 0 -> 517876 bytes
.../4/guide/index.html | 243 ++++-
.../4/guide/pages/domainClass.html | 17 +-
.../4/guide/pages/domainClassTest.html | 45 +-
.../4/guide/pages/v1Controller.html | 16 +-
.../4/guide/pages/v1FunctionalTests.html | 40 +-
.../4/guide/pages/v2Controller.html | 86 +-
.../4/guide/pages/v2FunctionalTests.html | 39 +-
.../4/guide/single.html | 243 ++++-
.../4/guide/version2.html | 125 ++-
.../4/guide/writingTheGrailsApp.html | 118 ++-
.../4/img/apptransportsecurity.png | Bin 0 -> 588981 bytes
.../4/img/choose_master-detail-application.png | Bin 0 -> 360097 bytes
.../4/img/choose_objective_c.png | Bin 0 -> 223487 bytes
.../4/img/connect_detail_iboutlets.png | Bin 0 -> 308725 bytes
.../4/img/create_new_project.png | Bin 0 -> 225197 bytes
.../4/img/ios-announcements-overview.jpeg | Bin 0 -> 556369 bytes
.../4/img/overview.jpeg | Bin 0 -> 581025 bytes
.../4/img/segue.jpeg | Bin 0 -> 516770 bytes
.../4/img/version2overview.jpeg | Bin 0 -> 514128 bytes
.../4/guide/index.html | 243 ++++-
.../4/guide/pages/domainClass.html | 17 +-
.../4/guide/pages/domainClassTest.html | 45 +-
.../4/guide/pages/v1Controller.html | 16 +-
.../4/guide/pages/v1FunctionalTests.html | 40 +-
.../4/guide/pages/v2Controller.html | 86 +-
.../4/guide/pages/v2FunctionalTests.html | 39 +-
.../4/guide/single.html | 243 ++++-
.../4/guide/version2.html | 125 ++-
.../4/guide/writingTheGrailsApp.html | 118 ++-
.../4/img/choose_master-detail-application.png | Bin 0 -> 360097 bytes
.../4/img/choose_swift.png | Bin 0 -> 233549 bytes
.../4/img/connect_detail_iboutlets.png | Bin 0 -> 308725 bytes
.../4/img/create_new_project.png | Bin 0 -> 225197 bytes
.../4/img/ios-announcements-overview.jpeg | Bin 0 -> 553101 bytes
.../4/img/overview.jpeg | Bin 0 -> 581025 bytes
.../4/img/segue.jpeg | Bin 0 -> 516770 bytes
.../4/img/version2overview.jpeg | Bin 0 -> 514128 bytes
.../command-objects-and-forms/3/guide/index.html | 203 +++-
.../3/guide/pages/addCreateAndSaveActions.html | 81 +-
.../3/guide/pages/addEditAndUpdateActions.html | 107 +-
.../3/guide/pages/createTheController.html | 15 +-
.../command-objects-and-forms/3/guide/single.html | 203 +++-
.../3/guide/writingTheApp.html | 203 +++-
.../command-objects-and-forms/4/guide/index.html | 264 ++++-
.../4/guide/pages/addCreateAndSaveActions.html | 138 ++-
.../4/guide/pages/addEditAndUpdateActions.html | 108 +-
.../4/guide/pages/createTheController.html | 18 +-
.../command-objects-and-forms/4/guide/single.html | 264 ++++-
.../4/guide/writingTheApp.html | 264 ++++-
.../3/guide/controllers.html | 117 +-
.../3/guide/domainClasses.html | 86 +-
.../3/guide/index.html | 322 +++++-
.../3/guide/pages/addJavascriptAsset.html | 8 +-
.../3/guide/pages/bootstrap.html | 21 +-
.../3/guide/pages/console.html | 18 +-
.../3/guide/pages/expandDomain.html | 47 +-
.../3/guide/pages/gspLinks.html | 4 +-
.../3/guide/pages/respond.html | 6 +-
.../3/guide/pages/staticScaffolding.html | 111 +-
.../3/guide/services.html | 31 +-
.../3/guide/single.html | 322 +++++-
.../3/guide/testing.html | 31 +-
.../3/guide/views.html | 57 +-
.../3/img/dbconsole-2.png | Bin 0 -> 129412 bytes
.../3/img/dbconsole.png | Bin 0 -> 41945 bytes
.../4/guide/controllers.html | 117 +-
.../4/guide/domainClasses.html | 92 +-
.../4/guide/index.html | 326 +++++-
.../4/guide/pages/addJavascriptAsset.html | 8 +-
.../4/guide/pages/bootstrap.html | 27 +-
.../4/guide/pages/console.html | 18 +-
.../4/guide/pages/expandDomain.html | 47 +-
.../4/guide/pages/gspLinks.html | 4 +-
.../4/guide/pages/respond.html | 6 +-
.../4/guide/pages/staticScaffolding.html | 111 +-
.../4/guide/services.html | 29 +-
.../4/guide/single.html | 326 +++++-
.../4/guide/testing.html | 31 +-
.../4/guide/views.html | 57 +-
.../4/img/dbconsole-2.png | Bin 0 -> 129412 bytes
.../4/img/dbconsole.png | Bin 0 -> 41945 bytes
.../6/guide/controllers.html | 117 +-
.../6/guide/domainClasses.html | 92 +-
.../6/guide/index.html | 322 +++++-
.../6/guide/pages/addJavascriptAsset.html | 8 +-
.../6/guide/pages/bootstrap.html | 27 +-
.../6/guide/pages/console.html | 18 +-
.../6/guide/pages/expandDomain.html | 47 +-
.../6/guide/pages/gspLinks.html | 4 +-
.../6/guide/pages/respond.html | 6 +-
.../6/guide/pages/staticScaffolding.html | 111 +-
.../6/guide/services.html | 25 +-
.../6/guide/single.html | 322 +++++-
.../6/guide/testing.html | 31 +-
.../6/guide/views.html | 57 +-
guides/gorm-event-listeners/3/guide/index.html | 561 +++++++++-
.../gorm-event-listeners/3/guide/pages/async.html | 190 +++-
.../3/guide/pages/dataServices.html | 38 +-
.../gorm-event-listeners/3/guide/pages/sync.html | 295 +++++-
guides/gorm-event-listeners/3/guide/single.html | 561 +++++++++-
.../3/guide/writingTheApp.html | 561 +++++++++-
guides/gorm-event-listeners/4/guide/index.html | 560 +++++++++-
.../gorm-event-listeners/4/guide/pages/async.html | 189 +++-
.../4/guide/pages/dataServices.html | 38 +-
.../gorm-event-listeners/4/guide/pages/sync.html | 295 +++++-
guides/gorm-event-listeners/4/guide/single.html | 560 +++++++++-
.../4/guide/writingTheApp.html | 560 +++++++++-
.../4/guide/configuringGormGraphQL.html | 76 +-
.../4/guide/index.html | 156 ++-
.../4/guide/installApollo.html | 24 +-
.../4/guide/mutations.html | 56 +-
.../4/guide/single.html | 156 ++-
.../4/img/devtools.png | Bin 0 -> 115515 bytes
.../4/img/graphql.png | Bin 0 -> 310768 bytes
guides/gorm-ratpack/3/guide/index.html | 153 ++-
guides/gorm-ratpack/3/guide/pages/application.html | 24 +-
.../3/guide/pages/configuringTheApp.html | 8 +-
.../3/guide/pages/creatingTestData.html | 31 +-
.../3/guide/pages/creatingTheControllers.html | 18 +-
.../3/guide/pages/creatingTheDomain.html | 37 +-
.../3/guide/pages/creatingTheServices.html | 35 +-
guides/gorm-ratpack/3/guide/single.html | 153 ++-
.../gorm-ratpack/3/guide/writingTheAppPartOne.html | 153 ++-
guides/gorm-ratpack/4/guide/gettingStarted.html | 4 +-
guides/gorm-ratpack/4/guide/index.html | 144 ++-
guides/gorm-ratpack/4/guide/pages/application.html | 24 +-
.../4/guide/pages/configuringTheApp.html | 3 +-
.../4/guide/pages/creatingTestData.html | 31 +-
.../4/guide/pages/creatingTheControllers.html | 10 +-
.../4/guide/pages/creatingTheDomain.html | 37 +-
.../4/guide/pages/creatingTheServices.html | 35 +-
guides/gorm-ratpack/4/guide/pages/howto.html | 4 +-
guides/gorm-ratpack/4/guide/single.html | 144 ++-
.../gorm-ratpack/4/guide/writingTheAppPartOne.html | 140 ++-
.../4/guide/configuringTheApp.html | 51 +-
guides/gorm-without-grails/4/guide/index.html | 257 ++++-
.../4/guide/pages/application.html | 11 +-
.../4/guide/pages/creatingTestData.html | 57 +-
.../4/guide/pages/creatingTheControllers.html | 76 +-
.../4/guide/pages/creatingTheDomain.html | 37 +-
.../4/guide/pages/creatingTheServices.html | 36 +-
guides/gorm-without-grails/4/guide/single.html | 257 ++++-
.../4/guide/writingTheAppPartOne.html | 206 +++-
.../gorm-without-grails/4/img/buildAndMetadata.png | Bin 0 -> 300685 bytes
guides/gorm-without-grails/4/img/consoleLogin.png | Bin 0 -> 43480 bytes
.../gorm-without-grails/4/img/initialStartup.png | Bin 0 -> 278496 bytes
.../gorm-without-grails/4/img/loggedInConsole.png | Bin 0 -> 130521 bytes
.../grails-as-docker-container/3/guide/index.html | 77 +-
.../3/guide/pages/appEntry.html | 3 +-
.../3/guide/pages/gradle.html | 25 +-
.../3/guide/pages/gradleDockerTasks.html | 49 +-
.../grails-as-docker-container/3/guide/single.html | 77 +-
.../3/guide/writing.html | 77 +-
guides/grails-as-docker-container/3/img/docker.png | Bin 0 -> 159266 bytes
.../grails-as-docker-container/3/img/kitematic.png | Bin 0 -> 195555 bytes
.../grails-as-docker-container/4/guide/index.html | 77 +-
.../4/guide/pages/appEntry.html | 3 +-
.../4/guide/pages/gradle.html | 25 +-
.../4/guide/pages/gradleDockerTasks.html | 49 +-
.../grails-as-docker-container/4/guide/single.html | 77 +-
.../4/guide/writing.html | 77 +-
guides/grails-as-docker-container/4/img/docker.png | Bin 0 -> 159266 bytes
.../grails-as-docker-container/4/img/kitematic.png | Bin 0 -> 195555 bytes
guides/grails-async-promises/3/img/cities.png | Bin 0 -> 95791 bytes
guides/grails-async-promises/3/img/runasync.png | Bin 0 -> 160236 bytes
guides/grails-async-promises/3/img/runsync.png | Bin 0 -> 170327 bytes
guides/grails-async-promises/4/img/cities.png | Bin 0 -> 95791 bytes
guides/grails-async-promises/4/img/runasync.png | Bin 0 -> 160236 bytes
guides/grails-async-promises/4/img/runsync.png | Bin 0 -> 170327 bytes
guides/grails-basicauth/3/img/forgeDefault.png | Bin 0 -> 69501 bytes
guides/grails-basicauth/4/img/forgeDefault.png | Bin 0 -> 69501 bytes
guides/grails-code-coverage/3/img/report.png | Bin 0 -> 405034 bytes
guides/grails-code-coverage/4/img/report.png | Bin 0 -> 405034 bytes
guides/grails-codenarc/4/img/multiproject.png | Bin 0 -> 16050 bytes
guides/grails-codenarc/4/img/report1.png | Bin 0 -> 264969 bytes
guides/grails-codenarc/4/img/report2.png | Bin 0 -> 76847 bytes
guides/grails-codenarc/4/img/report3.png | Bin 0 -> 170706 bytes
.../grails-controller-testing/3/guide/index.html | 309 +++++-
.../3/guide/pages/allowedMethods.html | 37 +-
.../3/guide/pages/domainClassAndScaffold.html | 14 +-
.../3/guide/pages/functionalTestController.html | 37 +-
.../3/guide/pages/unitTestIndex.html | 60 +-
.../3/guide/pages/unitTestSave.html | 165 ++-
.../grails-controller-testing/3/guide/single.html | 309 +++++-
.../3/guide/writingTheApp.html | 309 +++++-
.../grails-controller-testing/4/guide/index.html | 334 +++++-
.../4/guide/pages/allowedMethods.html | 38 +-
.../4/guide/pages/domainClassAndScaffold.html | 14 +-
.../4/guide/pages/functionalTestController.html | 61 +-
.../4/guide/pages/unitTestIndex.html | 60 +-
.../4/guide/pages/unitTestSave.html | 165 ++-
.../grails-controller-testing/4/guide/single.html | 334 +++++-
.../4/guide/writingTheApp.html | 334 +++++-
.../3/guide/index.html | 538 +++++++++-
.../3/guide/pages/controller.html | 31 +-
.../3/guide/pages/domain.html | 28 +-
.../3/guide/pages/security.html | 219 +++-
.../3/guide/pages/tenantResolverByJWT.html | 153 ++-
.../3/guide/pages/tenantResolverByUser.html | 15 +-
.../3/guide/pages/test.html | 92 +-
.../3/guide/single.html | 538 +++++++++-
.../3/guide/writingTheApp.html | 538 +++++++++-
.../4/guide/index.html | 544 +++++++++-
.../4/guide/pages/controller.html | 31 +-
.../4/guide/pages/domain.html | 26 +-
.../4/guide/pages/security.html | 222 +++-
.../4/guide/pages/tenantResolverByJWT.html | 156 ++-
.../4/guide/pages/tenantResolverByUser.html | 15 +-
.../4/guide/pages/test.html | 94 +-
.../4/guide/single.html | 544 +++++++++-
.../4/guide/writingTheApp.html | 544 +++++++++-
.../grails-database-migration/3/guide/index.html | 277 +++--
.../3/guide/pages/addAttributes.html | 45 +-
.../3/guide/pages/baseline.html | 31 +-
.../3/guide/pages/domainClassAndController.html | 12 +-
.../3/guide/pages/installDatabase.html | 16 +-
.../3/guide/pages/installPlugin.html | 14 +-
.../3/guide/pages/makeColumnNullable.html | 29 +-
.../3/guide/pages/redesignTables.html | 130 ++-
.../grails-database-migration/3/guide/single.html | 277 +++--
.../3/guide/writingTheApp.html | 277 +++--
.../grails-database-migration/3/img/migration1.jpg | Bin 0 -> 44781 bytes
.../grails-database-migration/3/img/migration2.jpg | Bin 0 -> 64426 bytes
.../grails-database-migration/3/img/migration3.jpg | Bin 0 -> 80346 bytes
.../grails-database-migration/4/guide/index.html | 276 ++++-
.../4/guide/pages/addAttributes.html | 45 +-
.../4/guide/pages/baseline.html | 31 +-
.../4/guide/pages/domainClassAndController.html | 12 +-
.../4/guide/pages/installDatabase.html | 16 +-
.../4/guide/pages/installPlugin.html | 13 +-
.../4/guide/pages/makeColumnNullable.html | 29 +-
.../4/guide/pages/redesignTables.html | 130 ++-
.../grails-database-migration/4/guide/single.html | 276 ++++-
.../4/guide/writingTheApp.html | 276 ++++-
.../grails-database-migration/4/img/migration1.jpg | Bin 0 -> 44781 bytes
.../grails-database-migration/4/img/migration2.jpg | Bin 0 -> 64426 bytes
.../grails-database-migration/4/img/migration3.jpg | Bin 0 -> 80346 bytes
.../grails-database-migration/6/guide/index.html | 202 +++-
.../6/guide/pages/addAttributes.html | 45 +-
.../6/guide/pages/baseline.html | 31 +-
.../6/guide/pages/domainClassAndController.html | 12 +-
.../6/guide/pages/installDatabase.html | 16 +-
.../6/guide/pages/installPlugin.html | 12 +-
.../6/guide/pages/makeColumnNullable.html | 29 +-
.../6/guide/pages/redesignTables.html | 57 +-
.../grails-database-migration/6/guide/single.html | 202 +++-
.../6/guide/writingTheApp.html | 202 +++-
.../grails-database-migration/6/img/migration1.jpg | Bin 0 -> 44781 bytes
.../grails-database-migration/6/img/migration2.jpg | Bin 0 -> 64426 bytes
.../grails-database-migration/6/img/migration3.jpg | Bin 0 -> 80346 bytes
.../4/guide/index.html | 137 ++-
.../4/guide/pages/domainClass.html | 9 +-
.../4/guide/pages/gradle.html | 25 +-
.../4/guide/pages/gradleDockerTasks.html | 71 +-
.../4/guide/pages/postgresql.html | 10 +-
.../4/guide/single.html | 137 ++-
.../4/guide/usingOnlyDocker.html | 22 +-
.../4/guide/writing.html | 115 +-
.../grails-docker-external-services/4/img/db.png | Bin 0 -> 79075 bytes
.../4/img/docker.png | Bin 0 -> 159266 bytes
.../4/img/kitematic.png | Bin 0 -> 195555 bytes
.../3/guide/index.html | 227 +++-
.../3/guide/pages/bootstrap.html | 21 +-
.../3/guide/pages/configuration.html | 26 +-
.../3/guide/pages/domain.html | 8 +-
.../3/guide/pages/gormevents.html | 69 +-
.../3/guide/pages/test.html | 103 +-
.../3/guide/single.html | 227 +++-
.../3/guide/writingTheApp.html | 227 +++-
.../4/guide/index.html | 225 +++-
.../4/guide/pages/bootstrap.html | 21 +-
.../4/guide/pages/configuration.html | 22 +-
.../4/guide/pages/domain.html | 8 +-
.../4/guide/pages/gormevents.html | 69 +-
.../4/guide/pages/test.html | 105 +-
.../4/guide/single.html | 225 +++-
.../4/guide/writingTheApp.html | 225 +++-
guides/grails-elasticbeanstalk/4/guide/deploy.html | 2 +-
guides/grails-elasticbeanstalk/4/guide/index.html | 45 +-
guides/grails-elasticbeanstalk/4/guide/single.html | 45 +-
.../4/guide/writingTheApp.html | 43 +-
.../4/img/elasticbeanstalk1.png | Bin 0 -> 184630 bytes
.../4/img/elasticbeanstalk2.png | Bin 0 -> 126689 bytes
.../4/img/elasticbeanstalk3.png | Bin 0 -> 223514 bytes
.../4/img/elasticbeanstalk4.png | Bin 0 -> 418697 bytes
.../4/img/elasticbeanstalk5.png | Bin 0 -> 351148 bytes
.../4/img/elasticbeanstalk6.png | Bin 0 -> 152207 bytes
.../4/img/elasticbeanstalk7.png | Bin 0 -> 385104 bytes
.../4/img/elasticbeanstalk8.png | Bin 0 -> 222858 bytes
.../4/img/elasticbeanstalk9.png | Bin 0 -> 52226 bytes
guides/grails-elasticsearch/4/guide/index.html | 319 +++++-
.../4/guide/pages/bootstrap.html | 100 +-
.../4/guide/pages/controller.html | 60 +-
.../grails-elasticsearch/4/guide/pages/domain.html | 16 +-
.../4/guide/pages/functionalTest.html | 41 +-
.../4/guide/pages/gormDataService.html | 12 +-
.../4/guide/pages/install.html | 15 +-
.../4/guide/pages/jsonview.html | 12 +-
.../4/guide/pages/logchanges.html | 2 +-
.../4/guide/pages/modifyUrlMappings.html | 21 +-
.../4/guide/pages/searchable.html | 6 +-
.../4/guide/pages/service.html | 34 +-
guides/grails-elasticsearch/4/guide/single.html | 319 +++++-
guides/grails-elasticsearch/4/guide/writing.html | 319 +++++-
guides/grails-email/3/img/forgeDefault.png | Bin 0 -> 69501 bytes
guides/grails-email/4/img/forgeDefault.png | Bin 0 -> 69501 bytes
guides/grails-events/3/guide/index.html | 304 +++++-
.../grails-events/3/guide/pages/controllers.html | 68 +-
.../grails-events/3/guide/pages/dataServices.html | 20 +-
guides/grails-events/3/guide/pages/domain.html | 26 +-
guides/grails-events/3/guide/pages/services.html | 74 +-
guides/grails-events/3/guide/pages/testing.html | 62 +-
guides/grails-events/3/guide/pages/urlMapping.html | 3 +-
guides/grails-events/3/guide/pages/view.html | 51 +-
guides/grails-events/3/guide/single.html | 304 +++++-
guides/grails-events/3/guide/writingTheApp.html | 304 +++++-
guides/grails-events/4/guide/index.html | 304 +++++-
.../grails-events/4/guide/pages/controllers.html | 68 +-
.../grails-events/4/guide/pages/dataServices.html | 20 +-
guides/grails-events/4/guide/pages/domain.html | 26 +-
guides/grails-events/4/guide/pages/services.html | 74 +-
guides/grails-events/4/guide/pages/testing.html | 62 +-
guides/grails-events/4/guide/pages/urlMapping.html | 3 +-
guides/grails-events/4/guide/pages/view.html | 51 +-
guides/grails-events/4/guide/single.html | 304 +++++-
guides/grails-events/4/guide/writingTheApp.html | 304 +++++-
.../3/guide/index.html | 56 +-
.../3/guide/pages/gebConfig.html | 36 +-
.../3/guide/pages/test.html | 20 +-
.../3/guide/single.html | 56 +-
.../3/guide/writingTheApp.html | 56 +-
.../3/img/welcomeToGrails.png | Bin 0 -> 229737 bytes
.../4/guide/index.html | 53 +-
.../4/guide/pages/gebConfig.html | 32 +-
.../4/guide/pages/test.html | 21 +-
.../4/guide/single.html | 53 +-
.../4/guide/writingTheApp.html | 53 +-
.../4/img/welcomeToGrails.png | Bin 0 -> 229737 bytes
guides/grails-google-cloud/4/guide/cloudSQL.html | 12 +-
.../grails-google-cloud/4/guide/cloudStorage.html | 219 +++-
.../4/guide/googleAppEngine.html | 21 +-
guides/grails-google-cloud/4/guide/index.html | 303 +++++-
guides/grails-google-cloud/4/guide/logging.html | 14 +-
.../4/guide/pages/datasourceUsingCloudSQL.html | 12 +-
.../4/guide/pages/domainClass.html | 17 +-
.../4/guide/pages/googleAppEngineGradlePlugin.html | 15 +-
.../4/guide/pages/seedData.html | 20 +-
.../4/guide/pages/springBootJetty.html | 6 +-
guides/grails-google-cloud/4/guide/single.html | 303 +++++-
.../grails-google-cloud/4/guide/writingTheApp.html | 37 +-
guides/grails-google-cloud/4/img/cloudsql-1.png | Bin 0 -> 362617 bytes
guides/grails-google-cloud/4/img/cloudsql-10.png | Bin 0 -> 241856 bytes
guides/grails-google-cloud/4/img/cloudsql-11.png | Bin 0 -> 301140 bytes
guides/grails-google-cloud/4/img/cloudsql-12.png | Bin 0 -> 514671 bytes
guides/grails-google-cloud/4/img/cloudsql-13.png | Bin 0 -> 664737 bytes
guides/grails-google-cloud/4/img/cloudsql-2.png | Bin 0 -> 151305 bytes
guides/grails-google-cloud/4/img/cloudsql-3.png | Bin 0 -> 316434 bytes
guides/grails-google-cloud/4/img/cloudsql-4.png | Bin 0 -> 174609 bytes
guides/grails-google-cloud/4/img/cloudsql-5.png | Bin 0 -> 406594 bytes
guides/grails-google-cloud/4/img/cloudsql-6.png | Bin 0 -> 298585 bytes
guides/grails-google-cloud/4/img/cloudsql-7.png | Bin 0 -> 313827 bytes
guides/grails-google-cloud/4/img/cloudsql-8.png | Bin 0 -> 517090 bytes
guides/grails-google-cloud/4/img/cloudsql-9.png | Bin 0 -> 617748 bytes
guides/grails-google-cloud/4/img/cloudsqlapi-1.png | Bin 0 -> 271512 bytes
.../grails-google-cloud/4/img/cloudstorage-1.png | Bin 0 -> 362617 bytes
.../grails-google-cloud/4/img/cloudstorage-2.png | Bin 0 -> 472524 bytes
.../grails-google-cloud/4/img/cloudstorage-3.png | Bin 0 -> 359130 bytes
.../grails-google-cloud/4/img/cloudstorage-4.png | Bin 0 -> 363619 bytes
.../grails-google-cloud/4/img/cloudstorage-5.png | Bin 0 -> 200381 bytes
.../grails-google-cloud/4/img/cloudstorage-6.png | Bin 0 -> 445110 bytes
.../grails-google-cloud/4/img/create-project-2.png | Bin 0 -> 115226 bytes
.../grails-google-cloud/4/img/create-project.png | Bin 0 -> 88965 bytes
guides/grails-google-cloud/4/img/logs.png | Bin 0 -> 102440 bytes
.../grails-google-cloud/4/img/welcometograils.png | Bin 0 -> 339367 bytes
.../4/guide/googleCloudSettings.html | 45 +-
guides/grails-google-home/4/guide/index.html | 607 ++++++++++-
.../4/guide/pages/colorRequestHandlerFactory.html | 381 ++++++-
.../4/guide/pages/googleActionsApi.html | 45 +-
.../4/guide/pages/googleActionsJavaSdk.html | 2 +-
.../4/guide/pages/grailsResponseHandler.html | 179 +++-
guides/grails-google-home/4/guide/single.html | 607 ++++++++++-
.../grails-google-home/4/guide/writingTheApp.html | 562 +++++++++-
guides/grails-google-home/4/img/appinfo.png | Bin 0 -> 314398 bytes
guides/grails-google-home/4/img/appinfo2.png | Bin 0 -> 325128 bytes
.../4/img/enablegoogleactions.png | Bin 0 -> 261499 bytes
guides/grails-google-home/4/img/gactions-cli.png | Bin 0 -> 80243 bytes
.../4/img/googleactionsconsole.png | Bin 0 -> 162663 bytes
guides/grails-google-home/4/img/scenarioA.jpeg | Bin 0 -> 414670 bytes
guides/grails-google-home/4/img/scenarioB.jpeg | Bin 0 -> 295841 bytes
.../4/img/test-brigther-color.png | Bin 0 -> 100131 bytes
guides/grails-google-home/4/img/test-default.png | Bin 0 -> 111640 bytes
guides/grails-google-home/4/img/test1.png | Bin 0 -> 180021 bytes
guides/grails-google-home/4/img/test2.png | Bin 0 -> 44451 bytes
guides/grails-google-home/4/img/useactionssdk.png | Bin 0 -> 230809 bytes
.../grails-gorm-data-services/3/guide/index.html | 362 ++++++-
.../3/guide/japqlProjection.html | 76 +-
.../3/guide/japqlQuery.html | 55 +-
.../3/guide/pages/domainObjects.html | 28 +-
.../3/guide/pages/dynamicFinders.html | 12 +-
.../3/guide/pages/joinQuery.html | 90 +-
.../3/guide/pages/propertyProjection.html | 16 +-
.../3/guide/pages/saveOperation.html | 15 +-
.../3/guide/pages/unitTest.html | 72 +-
.../grails-gorm-data-services/3/guide/single.html | 362 ++++++-
.../3/guide/usingGormDataServices.html | 205 +++-
.../3/guide/writingTheApp.html | 28 +-
.../grails-gorm-data-services/4/guide/index.html | 360 ++++++-
.../4/guide/japqlProjection.html | 76 +-
.../4/guide/japqlQuery.html | 55 +-
.../4/guide/pages/domainObjects.html | 28 +-
.../4/guide/pages/dynamicFinders.html | 12 +-
.../4/guide/pages/joinQuery.html | 90 +-
.../4/guide/pages/propertyProjection.html | 16 +-
.../4/guide/pages/saveOperation.html | 15 +-
.../4/guide/pages/unitTest.html | 70 +-
.../grails-gorm-data-services/4/guide/single.html | 360 ++++++-
.../4/guide/usingGormDataServices.html | 203 +++-
.../4/guide/writingTheApp.html | 28 +-
guides/grails-javamelody/4/guide/index.html | 223 +++-
guides/grails-javamelody/4/guide/melody.html | 11 +-
.../4/guide/pages/originalAppGrails.html | 212 +++-
.../grails-javamelody/4/guide/runningTheApp.html | 212 +++-
guides/grails-javamelody/4/guide/single.html | 223 +++-
guides/grails-javamelody/4/img/booksHome.png | Bin 0 -> 786448 bytes
.../4/img/melodyCombinedMonitoring.png | Bin 0 -> 187953 bytes
guides/grails-javamelody/4/img/melodyHome.png | Bin 0 -> 167909 bytes
.../4/img/melodyIndependentMonitoring.png | Bin 0 -> 216276 bytes
guides/grails-logicaldelete/3/guide/index.html | 406 ++++++-
.../3/guide/pages/bootstrap.html | 120 ++-
.../3/guide/pages/controllers.html | 53 +-
.../grails-logicaldelete/3/guide/pages/domain.html | 17 +-
.../3/guide/pages/install.html | 2 +-
.../3/guide/pages/services.html | 47 +-
.../grails-logicaldelete/3/guide/pages/test.html | 92 +-
.../3/guide/pages/urlMappings.html | 17 +-
.../grails-logicaldelete/3/guide/pages/views.html | 58 +-
.../3/guide/runningTheApp.html | 406 ++++++-
guides/grails-logicaldelete/3/guide/single.html | 406 ++++++-
guides/grails-logicaldelete/3/img/undo.png | Bin 0 -> 237084 bytes
guides/grails-logicaldelete/4/guide/index.html | 406 ++++++-
.../4/guide/pages/bootstrap.html | 120 ++-
.../4/guide/pages/controllers.html | 53 +-
.../grails-logicaldelete/4/guide/pages/domain.html | 17 +-
.../4/guide/pages/install.html | 2 +-
.../4/guide/pages/services.html | 47 +-
.../grails-logicaldelete/4/guide/pages/test.html | 92 +-
.../4/guide/pages/urlMappings.html | 17 +-
.../grails-logicaldelete/4/guide/pages/views.html | 58 +-
.../4/guide/runningTheApp.html | 406 ++++++-
guides/grails-logicaldelete/4/guide/single.html | 406 ++++++-
guides/grails-logicaldelete/4/img/undo.png | Bin 0 -> 237084 bytes
guides/grails-micronaut-http/5/guide/index.html | 159 ++-
.../5/guide/micronautHttpClient.html | 74 +-
.../5/guide/pages/declarativeClient.html | 27 +-
.../5/guide/pages/lowLevelClient.html | 45 +-
guides/grails-micronaut-http/5/guide/single.html | 159 ++-
guides/grails-micronaut-http/5/guide/test.html | 85 +-
guides/grails-micronaut-kafka/4/guide/index.html | 120 ++-
.../4/guide/originalAppGrails.html | 120 ++-
guides/grails-micronaut-kafka/4/guide/single.html | 120 ++-
guides/grails-mock-basics/3/guide/index.html | 231 +++-
.../3/guide/pages/domainClasses.html | 26 +-
.../3/guide/pages/dynamicFinderService.html | 15 +-
.../3/guide/pages/dynamicFinderServiceTests.html | 32 +-
.../3/guide/pages/gormHibernate.html | 13 +-
.../3/guide/pages/gormService.html | 18 +-
.../3/guide/pages/integrationTests.html | 39 +-
.../3/guide/pages/serviceWithCollaborators.html | 42 +-
.../3/guide/pages/testEmailWithMock.html | 58 +-
guides/grails-mock-basics/3/guide/single.html | 231 +++-
.../grails-mock-basics/3/guide/writingTheApp.html | 231 +++-
guides/grails-mock-basics/4/guide/index.html | 231 +++-
.../4/guide/pages/domainClasses.html | 26 +-
.../4/guide/pages/dynamicFinderService.html | 15 +-
.../4/guide/pages/dynamicFinderServiceTests.html | 32 +-
.../4/guide/pages/gormHibernate.html | 13 +-
.../4/guide/pages/gormService.html | 18 +-
.../4/guide/pages/integrationTests.html | 39 +-
.../4/guide/pages/serviceWithCollaborators.html | 42 +-
.../4/guide/pages/testEmailWithMock.html | 58 +-
guides/grails-mock-basics/4/guide/single.html | 231 +++-
.../grails-mock-basics/4/guide/writingTheApp.html | 231 +++-
guides/grails-mock-http-server/3/img/apiKey.png | Bin 0 -> 123760 bytes
guides/grails-mock-http-server/3/img/homepage.png | Bin 0 -> 35100 bytes
guides/grails-mock-http-server/4/img/apiKey.png | Bin 0 -> 123760 bytes
guides/grails-mock-http-server/4/img/homepage.png | Bin 0 -> 35100 bytes
.../3/guide/index.html | 212 +++-
.../3/guide/mockLogging.html | 130 ++-
.../3/guide/pages/createPerson.html | 25 +-
.../3/guide/pages/offerAdvice.html | 31 +-
.../3/guide/pages/personDomainAndDataService.html | 24 +-
.../3/guide/pages/verifyLogsInController.html | 45 +-
.../3/guide/pages/verifyLogsInController2.html | 30 +-
.../3/guide/pages/verifyLogsInGroovyPogo.html | 49 +-
.../3/guide/single.html | 212 +++-
.../3/guide/writingTheApp.html | 82 +-
.../4/guide/index.html | 221 +++-
.../4/guide/mockLogging.html | 139 ++-
.../4/guide/pages/createPerson.html | 25 +-
.../4/guide/pages/offerAdvice.html | 31 +-
.../4/guide/pages/personDomainAndDataService.html | 24 +-
.../4/guide/pages/verifyLogsInController.html | 54 +-
.../4/guide/pages/verifyLogsInController2.html | 30 +-
.../4/guide/pages/verifyLogsInGroovyPogo.html | 49 +-
.../4/guide/single.html | 221 +++-
.../4/guide/writingTheApp.html | 82 +-
guides/grails-multi-datasource/3/guide/index.html | 490 ++++++++-
.../3/guide/pages/configuration.html | 24 +-
.../3/guide/pages/controllers.html | 106 +-
.../3/guide/pages/domain.html | 31 +-
.../3/guide/pages/services.html | 116 +-
.../3/guide/pages/test.html | 165 ++-
.../3/guide/pages/views.html | 48 +-
guides/grails-multi-datasource/3/guide/single.html | 490 ++++++++-
.../3/guide/writingTheApp.html | 490 ++++++++-
guides/grails-multi-datasource/3/img/graph.jpg | Bin 0 -> 226341 bytes
guides/grails-multi-datasource/4/guide/index.html | 433 +++++++-
.../4/guide/pages/configuration.html | 24 +-
.../4/guide/pages/controllers.html | 108 +-
.../4/guide/pages/domain.html | 31 +-
.../4/guide/pages/services.html | 116 +-
.../4/guide/pages/test.html | 106 +-
.../4/guide/pages/views.html | 48 +-
guides/grails-multi-datasource/4/guide/single.html | 433 +++++++-
.../4/guide/writingTheApp.html | 433 +++++++-
guides/grails-multi-datasource/4/img/graph.jpg | Bin 0 -> 226341 bytes
.../grails-multi-project-build/4/guide/index.html | 273 ++++-
.../4/guide/pages/code.html | 77 +-
.../4/guide/pages/createprojects.html | 16 +-
.../4/guide/pages/dependencies.html | 8 +-
.../4/guide/pages/featuresgradlefiles.html | 13 +-
.../4/guide/pages/rootgradle.html | 159 ++-
.../grails-multi-project-build/4/guide/single.html | 273 ++++-
.../4/guide/writingTheApp.html | 273 ++++-
.../4/img/codedependencies.png | Bin 0 -> 98001 bytes
.../4/img/dependencies.png | Bin 0 -> 48789 bytes
.../4/img/grails-multi-project-build-diagramm.png | Bin 0 -> 33858 bytes
.../4/img/grails-multi-project-build-ide.png | Bin 0 -> 163661 bytes
guides/grails-oauth-google/4/guide/appConfig.html | 199 +++-
.../4/guide/buildingTheApp.html | 344 +++++-
guides/grails-oauth-google/4/guide/index.html | 543 +++++++++-
.../4/guide/pages/authController.html | 70 +-
.../4/guide/pages/controllers.html | 62 +-
guides/grails-oauth-google/4/guide/pages/data.html | 116 +-
.../4/guide/pages/dependencies.html | 3 +-
.../grails-oauth-google/4/guide/pages/domain.html | 36 +-
.../grails-oauth-google/4/guide/pages/logout.html | 6 +-
.../4/guide/pages/oauthConfig.html | 79 +-
.../4/guide/pages/services.html | 59 +-
.../4/guide/pages/tokenReader.html | 41 +-
.../grails-oauth-google/4/guide/pages/views.html | 71 +-
guides/grails-oauth-google/4/guide/single.html | 543 +++++++++-
.../grails-oauth-google/4/img/addCredentials1.png | Bin 0 -> 89279 bytes
.../grails-oauth-google/4/img/addCredentials2.png | Bin 0 -> 112213 bytes
.../grails-oauth-google/4/img/addCredentials4.png | Bin 0 -> 97425 bytes
.../grails-oauth-google/4/img/addCredentials5.png | Bin 0 -> 76223 bytes
.../grails-oauth-google/4/img/apiConsoleSelect.png | Bin 0 -> 313881 bytes
guides/grails-oauth-google/4/img/apiSearch.png | Bin 0 -> 137723 bytes
.../grails-oauth-google/4/img/authorizedShow.png | Bin 0 -> 87167 bytes
.../grails-oauth-google/4/img/credentialsHome.png | Bin 0 -> 56740 bytes
.../grails-oauth-google/4/img/dashboardPostApi.png | Bin 0 -> 69179 bytes
.../4/img/dashboardPostProjectCreate.png | Bin 0 -> 73618 bytes
guides/grails-oauth-google/4/img/diagramm.png | Bin 0 -> 148480 bytes
guides/grails-oauth-google/4/img/enableApi.png | Bin 0 -> 62336 bytes
guides/grails-oauth-google/4/img/forgeDefault.png | Bin 0 -> 99541 bytes
.../4/img/googleCreateProject.png | Bin 0 -> 84745 bytes
.../4/img/googleDevelopersHome.png | Bin 0 -> 144104 bytes
guides/grails-oauth-google/4/img/googleSignin.png | Bin 0 -> 56255 bytes
guides/grails-oauth-google/4/img/homeScreen.png | Bin 0 -> 540997 bytes
guides/grails-oauth-google/4/img/loginMessage.png | Bin 0 -> 550549 bytes
guides/grails-oauth-google/4/img/logoutMessage.png | Bin 0 -> 26164 bytes
guides/grails-oauth-google/4/img/projectName.png | Bin 0 -> 25882 bytes
.../grails-oauth-google/4/img/searchGooglePlus.png | Bin 0 -> 61436 bytes
.../grails-oauth-google/4/img/unauthorizedShow.png | Bin 0 -> 88097 bytes
.../4/img/waitWhileProjectIsCreated.png | Bin 0 -> 37853 bytes
guides/grails-oauth-twitter/4/guide/appConfig.html | 198 +++-
.../4/guide/buildingTheApp.html | 376 ++++++-
guides/grails-oauth-twitter/4/guide/index.html | 574 +++++++++-
.../4/guide/pages/authController.html | 70 +-
.../4/guide/pages/controllers.html | 84 +-
.../grails-oauth-twitter/4/guide/pages/data.html | 116 +-
.../4/guide/pages/dependencies.html | 3 +-
.../grails-oauth-twitter/4/guide/pages/domain.html | 36 +-
.../grails-oauth-twitter/4/guide/pages/logout.html | 6 +-
.../4/guide/pages/oauthConfig.html | 78 +-
.../4/guide/pages/services.html | 63 +-
.../4/guide/pages/tokenReader.html | 41 +-
.../grails-oauth-twitter/4/guide/pages/views.html | 77 +-
guides/grails-oauth-twitter/4/guide/single.html | 574 +++++++++-
.../grails-oauth-twitter/4/img/authorizedShow.png | Bin 0 -> 87167 bytes
guides/grails-oauth-twitter/4/img/cookies.png | Bin 0 -> 563128 bytes
guides/grails-oauth-twitter/4/img/diagramm.png | Bin 0 -> 152186 bytes
guides/grails-oauth-twitter/4/img/forgeDefault.png | Bin 0 -> 99541 bytes
guides/grails-oauth-twitter/4/img/homeScreen.png | Bin 0 -> 442008 bytes
.../grails-oauth-twitter/4/img/logoutMessage.png | Bin 0 -> 26164 bytes
.../grails-oauth-twitter/4/img/twitterSignin.png | Bin 0 -> 132599 bytes
.../4/img/unauthorizedShow.png | Bin 0 -> 107012 bytes
.../grails-on-circleci-basics/4/guide/index.html | 90 +-
.../4/guide/integrateCircle.html | 27 +-
.../4/guide/pages/createCircleConfig.html | 27 +-
.../4/guide/pages/integrationTest.html | 19 +-
.../4/guide/pages/testLogging.html | 5 +-
.../4/guide/pages/unitTest.html | 13 +-
.../grails-on-circleci-basics/4/guide/single.html | 90 +-
.../4/guide/writingTheApp.html | 63 +-
guides/grails-on-circleci-basics/4/img/circle.png | Bin 0 -> 149984 bytes
guides/grails-on-github-actions/4/guide/index.html | 81 +-
.../4/guide/integrateGithubActions.html | 2 +-
.../4/guide/pages/integrationTest.html | 35 +-
.../4/guide/pages/testLogging.html | 5 +-
.../4/guide/pages/unitTest.html | 13 +-
.../grails-on-github-actions/4/guide/single.html | 81 +-
.../4/guide/writingTheApp.html | 79 +-
.../4/img/githubactions-1.png | Bin 0 -> 186951 bytes
.../4/img/githubactions-2.png | Bin 0 -> 505105 bytes
.../3/guide/createGitHubRepo.html | 22 +-
guides/grails-on-travis-basics/3/guide/index.html | 114 +-
.../3/guide/integrateTravis.html | 37 +-
.../3/guide/pages/createTravisConfig.html | 11 +-
.../3/guide/pages/delegateToScript.html | 26 +-
.../3/guide/pages/integrationTest.html | 21 +-
.../3/guide/pages/testLogging.html | 5 +-
.../3/guide/pages/unitTest.html | 13 +-
guides/grails-on-travis-basics/3/guide/single.html | 114 +-
.../3/guide/writingTheApp.html | 55 +-
guides/grails-on-travis-basics/3/img/appforge.png | Bin 0 -> 128844 bytes
guides/grails-on-travis-basics/3/img/travis.png | Bin 0 -> 102544 bytes
.../4/guide/createGitHubRepo.html | 22 +-
guides/grails-on-travis-basics/4/guide/index.html | 115 +-
.../4/guide/integrateTravis.html | 36 +-
.../4/guide/pages/createTravisConfig.html | 11 +-
.../4/guide/pages/delegateToScript.html | 25 +-
.../4/guide/pages/integrationTest.html | 21 +-
.../4/guide/pages/testLogging.html | 5 +-
.../4/guide/pages/unitTest.html | 13 +-
guides/grails-on-travis-basics/4/guide/single.html | 115 +-
.../4/guide/writingTheApp.html | 57 +-
guides/grails-on-travis-basics/4/img/appforge.png | Bin 0 -> 128844 bytes
guides/grails-on-travis-basics/4/img/travis.png | Bin 0 -> 102544 bytes
.../4/img/intellijwebinar.png | Bin 0 -> 1176969 bytes
guides/grails-rabbitmq/4/guide/index.html | 253 ++++-
.../grails-rabbitmq/4/guide/originalAppGrails.html | 253 ++++-
.../4/guide/pages/bookApplicationRabbit.html | 36 +-
guides/grails-rabbitmq/4/guide/single.html | 253 ++++-
guides/grails-rabbitmq/4/img/analyticsHome.png | Bin 0 -> 39974 bytes
guides/grails-rabbitmq/4/img/appsummary.png | Bin 0 -> 332317 bytes
guides/grails-rabbitmq/4/img/bookDescription.png | Bin 0 -> 105514 bytes
guides/grails-rabbitmq/4/img/booksHome.png | Bin 0 -> 786448 bytes
.../4/img/consumerMessageReceived.png | Bin 0 -> 101549 bytes
guides/grails-rabbitmq/4/img/dockerCmdSuccess.png | Bin 0 -> 58862 bytes
guides/grails-rabbitmq/4/img/dockerIcon.png | Bin 0 -> 2629 bytes
guides/grails-rabbitmq/4/img/dockerMenu.png | Bin 0 -> 41714 bytes
guides/grails-rabbitmq/4/img/emptyQueue.png | Bin 0 -> 205114 bytes
guides/grails-rabbitmq/4/img/kitematicRabbit.png | Bin 0 -> 576416 bytes
guides/grails-rabbitmq/4/img/messageQueues.png | Bin 0 -> 126487 bytes
guides/grails-rabbitmq/4/img/multiView.png | Bin 0 -> 54130 bytes
.../4/img/processingPrePostedMessages.png | Bin 0 -> 128299 bytes
guides/grails-rabbitmq/4/img/rabbitAdminHome.png | Bin 0 -> 152238 bytes
.../grails-rabbitmq/4/img/rabbitMQAdminLogin.png | Bin 0 -> 43335 bytes
guides/grails-rabbitmq/4/img/updatedAdminHome.png | Bin 0 -> 194823 bytes
guides/grails-rabbitmq/4/img/viewCountUpdate.png | Bin 0 -> 44863 bytes
.../4/guide/completeAngularApplication.html | 10 +-
guides/grails-restapi-angularjs/4/guide/index.html | 87 +-
.../4/guide/initialAngularApplication.html | 14 +-
.../4/guide/pages/domainClass.html | 59 +-
.../4/guide/pages/enableCors.html | 4 +-
.../grails-restapi-angularjs/4/guide/single.html | 87 +-
.../4/guide/writingTheApp.html | 63 +-
guides/grails-restapi-angularjs/4/img/complete.gif | Bin 0 -> 935108 bytes
guides/grails-restapi-angularjs/4/img/initial.gif | Bin 0 -> 1811422 bytes
.../grails-restapi-angularjs/4/img/treehouse.png | Bin 0 -> 46344 bytes
guides/grails-scheduled/3/guide/index.html | 209 +++-
.../3/guide/pages/createSimpleJob.html | 25 +-
.../grails-scheduled/3/guide/pages/logchanges.html | 2 +-
.../3/guide/pages/logicInService.html | 31 +-
.../3/guide/pages/logicInServiceTwo.html | 58 +-
.../3/guide/pages/runnableTasks.html | 23 +-
.../3/guide/pages/taskScheduler.html | 35 +-
.../3/guide/pages/triggerManually.html | 35 +-
guides/grails-scheduled/3/guide/single.html | 209 +++-
guides/grails-scheduled/3/guide/writing.html | 209 +++-
guides/grails-scheduled/4/guide/index.html | 209 +++-
.../4/guide/pages/createSimpleJob.html | 25 +-
.../grails-scheduled/4/guide/pages/logchanges.html | 2 +-
.../4/guide/pages/logicInService.html | 31 +-
.../4/guide/pages/logicInServiceTwo.html | 58 +-
.../4/guide/pages/runnableTasks.html | 23 +-
.../4/guide/pages/taskScheduler.html | 35 +-
.../4/guide/pages/triggerManually.html | 35 +-
guides/grails-scheduled/4/guide/single.html | 209 +++-
guides/grails-scheduled/4/guide/writing.html | 209 +++-
guides/grails-schwartz/4/guide/index.html | 257 ++++-
.../4/guide/pages/concurrentExecution.html | 57 +-
.../4/guide/pages/createSimpleJob.html | 19 +-
guides/grails-schwartz/4/guide/pages/install.html | 17 +-
.../grails-schwartz/4/guide/pages/logchanges.html | 2 +-
.../4/guide/pages/logicInService.html | 69 +-
.../4/guide/pages/triggerManually.html | 93 +-
guides/grails-schwartz/4/guide/single.html | 257 ++++-
guides/grails-schwartz/4/guide/writing.html | 257 ++++-
guides/grails-soap/3/guide/index.html | 360 ++++++-
.../3/guide/pages/controllerandview.html | 107 +-
guides/grails-soap/3/guide/pages/countries.html | 123 ++-
guides/grails-soap/3/guide/pages/soapLibrary.html | 52 +-
guides/grails-soap/3/guide/pages/test.html | 78 +-
guides/grails-soap/3/guide/single.html | 360 ++++++-
guides/grails-soap/3/guide/writingTheApp.html | 360 ++++++-
guides/grails-soap/4/guide/index.html | 350 +++++-
.../4/guide/pages/controllerandview.html | 107 +-
guides/grails-soap/4/guide/pages/countries.html | 123 ++-
guides/grails-soap/4/guide/pages/soapLibrary.html | 52 +-
guides/grails-soap/4/guide/pages/test.html | 68 +-
guides/grails-soap/4/guide/single.html | 350 +++++-
guides/grails-soap/4/guide/writingTheApp.html | 350 +++++-
.../3/guide/index.html | 535 +++++++++-
.../3/guide/pages/authenticationProvider.html | 88 +-
.../3/guide/pages/customLoginForm.html | 213 +++-
.../3/guide/pages/domainClasses.html | 63 +-
.../3/guide/pages/filter.html | 63 +-
.../3/guide/pages/securedController.html | 12 +-
.../3/guide/pages/seedData.html | 40 +-
.../3/guide/pages/test.html | 54 +-
.../3/guide/single.html | 535 +++++++++-
.../3/guide/writingTheApp.html | 535 +++++++++-
.../3/img/caja_espagna_coordenadas.jpg | Bin 0 -> 118189 bytes
.../3/img/login.png | Bin 0 -> 13748 bytes
.../4/guide/index.html | 570 +++++++++-
.../4/guide/pages/authenticationProvider.html | 88 +-
.../4/guide/pages/customLoginForm.html | 213 +++-
.../4/guide/pages/domainClasses.html | 63 +-
.../4/guide/pages/filter.html | 63 +-
.../4/guide/pages/securedController.html | 12 +-
.../4/guide/pages/seedData.html | 74 +-
.../4/guide/pages/test.html | 55 +-
.../4/guide/single.html | 570 +++++++++-
.../4/guide/writingTheApp.html | 570 +++++++++-
.../4/img/caja_espagna_coordenadas.jpg | Bin 0 -> 118189 bytes
.../4/img/login.png | Bin 0 -> 13748 bytes
.../grails-taglib-wyswyg-trix/3/guide/index.html | 85 +-
.../3/guide/pages/domainClass.html | 16 +-
.../3/guide/pages/taglib.html | 47 +-
.../3/guide/pages/trix.html | 4 +-
.../3/guide/pages/useTaglib.html | 18 +-
.../grails-taglib-wyswyg-trix/3/guide/single.html | 85 +-
.../3/guide/writingTheApp.html | 85 +-
guides/grails-taglib-wyswyg-trix/3/img/trix.png | Bin 0 -> 154033 bytes
.../grails-taglib-wyswyg-trix/4/guide/index.html | 85 +-
.../4/guide/pages/domainClass.html | 16 +-
.../4/guide/pages/taglib.html | 47 +-
.../4/guide/pages/trix.html | 4 +-
.../4/guide/pages/useTaglib.html | 18 +-
.../grails-taglib-wyswyg-trix/4/guide/single.html | 85 +-
.../4/guide/writingTheApp.html | 85 +-
guides/grails-taglib-wyswyg-trix/4/img/trix.png | Bin 0 -> 154033 bytes
.../3/guide/index.html | 272 ++++-
.../3/guide/pages/domainClass.html | 55 +-
.../3/guide/pages/unitTests.html | 217 +++-
.../3/guide/single.html | 272 ++++-
.../3/guide/writingTheApp.html | 272 ++++-
.../4/guide/index.html | 272 ++++-
.../4/guide/pages/domainClass.html | 55 +-
.../4/guide/pages/unitTests.html | 217 +++-
.../4/guide/single.html | 272 ++++-
.../4/guide/writingTheApp.html | 272 ++++-
guides/grails-test-security/3/guide/index.html | 591 ++++++++++-
.../3/guide/pages/apiController.html | 16 +-
.../3/guide/pages/domainClass.html | 13 +-
.../3/guide/pages/securingTheApp.html | 333 +++++-
.../3/guide/pages/testRestApi.html | 153 ++-
.../3/guide/pages/testing.html | 76 +-
guides/grails-test-security/3/guide/single.html | 591 ++++++++++-
.../3/guide/writingTheApp.html | 591 ++++++++++-
guides/grails-test-security/4/guide/index.html | 583 +++++++++-
.../4/guide/pages/apiController.html | 16 +-
.../4/guide/pages/domainClass.html | 13 +-
.../4/guide/pages/securingTheApp.html | 333 +++++-
.../4/guide/pages/testRestApi.html | 145 ++-
.../4/guide/pages/testing.html | 76 +-
guides/grails-test-security/4/guide/single.html | 583 +++++++++-
.../4/guide/writingTheApp.html | 583 +++++++++-
guides/grails-tvmlapp/3/guide/gettingStarted.html | 2 +-
guides/grails-tvmlapp/3/guide/index.html | 494 ++++++++-
.../grails-tvmlapp/3/guide/pages/architecture.html | 2 +-
.../3/guide/pages/controllerIndex.html | 106 +-
.../3/guide/pages/controllerShow.html | 176 ++-
.../grails-tvmlapp/3/guide/pages/domainClass.html | 136 ++-
.../3/guide/pages/internationalization.html | 17 +-
guides/grails-tvmlapp/3/guide/pages/tvBootURL.html | 21 +-
guides/grails-tvmlapp/3/guide/pages/tvmlUtils.html | 36 +-
guides/grails-tvmlapp/3/guide/single.html | 494 ++++++++-
guides/grails-tvmlapp/3/guide/writingTheApp.html | 492 ++++++++-
.../grails-tvmlapp/3/img/appTransportSecurity.png | Bin 0 -> 262947 bytes
guides/grails-tvmlapp/3/img/architecture.png | Bin 0 -> 144458 bytes
guides/grails-tvmlapp/3/img/productTemplate.png | Bin 0 -> 521365 bytes
guides/grails-tvmlapp/3/img/projectOptions.png | Bin 0 -> 152399 bytes
guides/grails-tvmlapp/3/img/stackTemplate.png | Bin 0 -> 816615 bytes
guides/grails-tvmlapp/3/img/tvBaseUrl.png | Bin 0 -> 806998 bytes
.../3/img/tvOSProjectTemplateTvml.png | Bin 0 -> 228224 bytes
guides/grails-tvmlapp/4/guide/gettingStarted.html | 2 +-
guides/grails-tvmlapp/4/guide/index.html | 544 +++++++++-
.../grails-tvmlapp/4/guide/pages/architecture.html | 2 +-
.../4/guide/pages/controllerIndex.html | 128 ++-
.../4/guide/pages/controllerShow.html | 191 +++-
.../grails-tvmlapp/4/guide/pages/domainClass.html | 149 ++-
.../4/guide/pages/internationalization.html | 17 +-
guides/grails-tvmlapp/4/guide/pages/tvBootURL.html | 21 +-
guides/grails-tvmlapp/4/guide/pages/tvmlUtils.html | 36 +-
guides/grails-tvmlapp/4/guide/single.html | 544 +++++++++-
guides/grails-tvmlapp/4/guide/writingTheApp.html | 542 +++++++++-
.../grails-tvmlapp/4/img/appTransportSecurity.png | Bin 0 -> 262947 bytes
guides/grails-tvmlapp/4/img/architecture.png | Bin 0 -> 144458 bytes
guides/grails-tvmlapp/4/img/productTemplate.png | Bin 0 -> 521365 bytes
guides/grails-tvmlapp/4/img/projectOptions.png | Bin 0 -> 152399 bytes
guides/grails-tvmlapp/4/img/stackTemplate.png | Bin 0 -> 816615 bytes
guides/grails-tvmlapp/4/img/tvBaseUrl.png | Bin 0 -> 806998 bytes
.../4/img/tvOSProjectTemplateTvml.png | Bin 0 -> 228224 bytes
guides/grails-upload-file/3/guide/awsS3.html | 135 ++-
guides/grails-upload-file/3/guide/index.html | 407 ++++++-
.../3/guide/pages/featuredImageCommand.html | 28 +-
.../3/guide/pages/hotelDomainClass.html | 14 +-
.../pages/hotelUploadFeaturedImageService.html | 74 +-
.../3/guide/pages/hotelView.html | 26 +-
.../3/guide/pages/increaseFileSize.html | 8 +-
.../3/guide/pages/loadByteArrayImage.html | 15 +-
.../3/guide/pages/pointOfInterestDomainClass.html | 11 +-
.../pointOfInterestUploadFeaturedImageService.html | 96 +-
.../3/guide/pages/pointOfInterestView.html | 26 +-
.../3/guide/pages/restaurantDomainClass.html | 17 +-
.../restaurantUploadFeaturedImageService.html | 46 +-
.../3/guide/pages/restaurantView.html | 25 +-
.../3/guide/pointOfInterest.html | 133 ++-
guides/grails-upload-file/3/guide/restaurant.html | 103 +-
guides/grails-upload-file/3/guide/single.html | 407 ++++++-
.../grails-upload-file/3/guide/writingTheApp.html | 36 +-
guides/grails-upload-file/3/img/bytearray.png | Bin 0 -> 50768 bytes
guides/grails-upload-file/3/img/s3.png | Bin 0 -> 54760 bytes
guides/grails-upload-file/3/img/transfer.png | Bin 0 -> 72652 bytes
guides/grails-upload-file/4/guide/awsS3.html | 135 ++-
guides/grails-upload-file/4/guide/index.html | 407 ++++++-
.../4/guide/pages/featuredImageCommand.html | 28 +-
.../4/guide/pages/hotelDomainClass.html | 14 +-
.../pages/hotelUploadFeaturedImageService.html | 74 +-
.../4/guide/pages/hotelView.html | 26 +-
.../4/guide/pages/increaseFileSize.html | 8 +-
.../4/guide/pages/loadByteArrayImage.html | 15 +-
.../4/guide/pages/pointOfInterestDomainClass.html | 11 +-
.../pointOfInterestUploadFeaturedImageService.html | 96 +-
.../4/guide/pages/pointOfInterestView.html | 26 +-
.../4/guide/pages/restaurantDomainClass.html | 17 +-
.../restaurantUploadFeaturedImageService.html | 46 +-
.../4/guide/pages/restaurantView.html | 25 +-
.../4/guide/pointOfInterest.html | 133 ++-
guides/grails-upload-file/4/guide/restaurant.html | 103 +-
guides/grails-upload-file/4/guide/single.html | 407 ++++++-
.../grails-upload-file/4/guide/writingTheApp.html | 36 +-
guides/grails-upload-file/4/img/bytearray.png | Bin 0 -> 50768 bytes
guides/grails-upload-file/4/img/s3.png | Bin 0 -> 54760 bytes
guides/grails-upload-file/4/img/transfer.png | Bin 0 -> 72652 bytes
guides/grails-vs-nodejs/4/guide/index.html | 1118 ++++++++++++++++++--
.../4/guide/pages/authentication.html | 222 +++-
.../4/guide/pages/deleteFlavorsByUser.html | 60 +-
.../4/guide/pages/flavorsByUser.html | 70 +-
guides/grails-vs-nodejs/4/guide/pages/gorm.html | 10 +-
guides/grails-vs-nodejs/4/guide/pages/react.html | 293 ++++-
.../4/guide/pages/saveFlavorsByUser.html | 78 +-
.../grails-vs-nodejs/4/guide/pages/seedData.html | 133 ++-
guides/grails-vs-nodejs/4/guide/pages/signup.html | 126 ++-
.../grails-vs-nodejs/4/guide/pages/websockets.html | 114 +-
guides/grails-vs-nodejs/4/guide/single.html | 1118 ++++++++++++++++++--
guides/grails-vs-nodejs/4/guide/writingTheApp.html | 1118 ++++++++++++++++++--
.../4/img/technologystack_grails.png | Bin 0 -> 130432 bytes
.../4/img/technologystack_node.png | Bin 0 -> 182890 bytes
guides/grails-vue-combined/4/guide/index.html | 33 +-
.../grails-vue-combined/4/guide/pages/config.html | 10 +-
.../grails-vue-combined/4/guide/pages/gradle.html | 17 +-
.../4/guide/pages/urlMappings.html | 6 +-
guides/grails-vue-combined/4/guide/single.html | 33 +-
.../grails-vue-combined/4/guide/writingTheApp.html | 33 +-
.../4/guide/evaluatingTheApp.html | 14 +-
.../4/guide/excessiveGarbageCollection.html | 15 +-
.../4/guide/excessiveHeapMemory.html | 8 +-
guides/grails-yourkit-profiling/4/guide/index.html | 47 +-
.../4/guide/inefficientDelete.html | 10 +-
.../grails-yourkit-profiling/4/guide/single.html | 47 +-
.../grails-yourkit-profiling/4/img/monitoring.jpg | Bin 0 -> 50183 bytes
guides/grails-yourkit-profiling/4/img/pinwheel.jpg | Bin 0 -> 2114 bytes
guides/grails_i18n/3/img/languages_drop_down.png | Bin 0 -> 75138 bytes
guides/grails_i18n/4/img/languages_drop_down.png | Bin 0 -> 75138 bytes
.../grails_url_mappings/4/guide/controllers.html | 101 +-
guides/grails_url_mappings/4/guide/index.html | 161 ++-
guides/grails_url_mappings/4/guide/single.html | 161 ++-
.../4/guide/splitUrlMappings.html | 31 +-
guides/grails_url_mappings/4/guide/testing.html | 29 +-
guides/neo4j-movies/4/guide/configuration.html | 13 +-
guides/neo4j-movies/4/guide/index.html | 190 +++-
.../4/guide/pages/byTitleEndpoint.html | 39 +-
guides/neo4j-movies/4/guide/pages/domainModel.html | 63 +-
guides/neo4j-movies/4/guide/pages/endpoints.html | 112 +-
.../neo4j-movies/4/guide/pages/graphEndpoint.html | 37 +-
.../neo4j-movies/4/guide/pages/searchEndpoint.html | 28 +-
guides/neo4j-movies/4/guide/pages/ui.html | 2 +-
guides/neo4j-movies/4/guide/single.html | 190 +++-
guides/neo4j-movies/4/guide/writingTheApp.html | 177 +++-
guides/neo4j-movies/4/img/neo4j-application.png | Bin 0 -> 121753 bytes
guides/neo4j-movies/4/img/neo4j-clickcode.png | Bin 0 -> 159936 bytes
guides/neo4j-movies/4/img/neo4j-localhost.png | Bin 0 -> 53687 bytes
guides/neo4j-movies/4/img/neo4j-password.png | Bin 0 -> 65824 bytes
guides/neo4j-movies/4/img/neo4j-playmovies.png | Bin 0 -> 65499 bytes
guides/neo4j-movies/4/img/neo4j-runcode.png | Bin 0 -> 122880 bytes
guides/neo4j-movies/4/img/neo4j-setpassword.png | Bin 0 -> 81277 bytes
guides/neo4j-movies/4/img/neo4j-start.png | Bin 0 -> 49414 bytes
guides/neo4j-movies/4/img/neo4j-ui.png | Bin 0 -> 231673 bytes
guides/react-combined/4/guide/index.html | 47 +-
guides/react-combined/4/guide/pages/config.html | 15 +-
guides/react-combined/4/guide/pages/gradle.html | 26 +-
.../react-combined/4/guide/pages/urlMappings.html | 6 +-
guides/react-combined/4/guide/single.html | 47 +-
guides/react-combined/4/guide/writingTheApp.html | 47 +-
guides/react-spring-security/4/guide/index.html | 614 ++++++++++-
.../4/guide/pages/appSecurityConfig.html | 76 +-
.../4/guide/pages/appState.html | 24 +-
.../4/guide/pages/bootstrapData.html | 22 +-
.../4/guide/pages/garage.html | 91 +-
.../4/guide/pages/inputChangeHandler.html | 9 +-
.../4/guide/pages/lifecycle.html | 18 +-
.../react-spring-security/4/guide/pages/login.html | 25 +-
.../4/guide/pages/logout.html | 5 +-
.../4/guide/pages/routing.html | 30 +-
.../4/guide/pages/serverDependencies.html | 3 +-
.../4/guide/pages/springSecurityConfig.html | 72 +-
.../4/guide/pages/writingAuth.html | 69 +-
.../4/guide/pages/writingErrorHandlers.html | 8 +-
.../4/guide/pages/writingResponseHandlers.html | 21 +-
.../4/guide/puttingItAllTogether.html | 343 +++++-
guides/react-spring-security/4/guide/server.html | 173 ++-
guides/react-spring-security/4/guide/single.html | 614 ++++++++++-
.../4/guide/writingTheSecurity.html | 98 +-
guides/react-spring-security/4/img/rest.png | Bin 0 -> 74973 bytes
guides/rest-hibernate/3/guide/index.html | 137 ++-
.../3/guide/pages/applyConstraints.html | 31 +-
guides/rest-hibernate/3/guide/pages/configure.html | 17 +-
.../rest-hibernate/3/guide/pages/controller.html | 9 +-
.../rest-hibernate/3/guide/pages/domainClass.html | 10 +-
guides/rest-hibernate/3/guide/pages/json.html | 19 +-
.../3/guide/pages/mapController.html | 7 +-
.../3/guide/pages/searchEndpoint.html | 18 +-
guides/rest-hibernate/3/guide/pages/testing.html | 26 +-
guides/rest-hibernate/3/guide/single.html | 137 ++-
guides/rest-hibernate/3/guide/writingTheApp.html | 137 ++-
guides/rest-hibernate/4/guide/index.html | 132 ++-
.../4/guide/pages/applyConstraints.html | 31 +-
guides/rest-hibernate/4/guide/pages/configure.html | 17 +-
.../rest-hibernate/4/guide/pages/controller.html | 10 +-
.../rest-hibernate/4/guide/pages/domainClass.html | 10 +-
guides/rest-hibernate/4/guide/pages/json.html | 19 +-
.../4/guide/pages/mapController.html | 7 +-
.../4/guide/pages/searchEndpoint.html | 15 +-
guides/rest-hibernate/4/guide/pages/testing.html | 23 +-
guides/rest-hibernate/4/guide/single.html | 132 ++-
guides/rest-hibernate/4/guide/writingTheApp.html | 132 ++-
guides/rest-mongodb/4/guide/index.html | 149 ++-
.../4/guide/pages/applyConstraints.html | 49 +-
guides/rest-mongodb/4/guide/pages/configure.html | 8 +-
guides/rest-mongodb/4/guide/pages/controller.html | 7 +-
guides/rest-mongodb/4/guide/pages/domainClass.html | 7 +-
guides/rest-mongodb/4/guide/pages/json.html | 19 +-
.../rest-mongodb/4/guide/pages/mapController.html | 7 +-
.../rest-mongodb/4/guide/pages/searchEndpoint.html | 18 +-
guides/rest-mongodb/4/guide/pages/testing.html | 32 +-
guides/rest-mongodb/4/guide/single.html | 149 ++-
guides/rest-mongodb/4/guide/writingTheApp.html | 149 ++-
guides/server-sent-events/3/guide/index.html | 91 +-
.../3/guide/pages/gradleConfig.html | 2 +-
.../3/guide/pages/implementController.html | 69 +-
.../3/guide/pages/implementTheClient.html | 20 +-
guides/server-sent-events/3/guide/single.html | 91 +-
.../server-sent-events/3/guide/writingTheApp.html | 91 +-
guides/server-sent-events/4/guide/index.html | 92 +-
.../4/guide/pages/gradleConfig.html | 3 +-
.../4/guide/pages/implementController.html | 69 +-
.../4/guide/pages/implementTheClient.html | 20 +-
guides/server-sent-events/4/guide/single.html | 92 +-
.../server-sent-events/4/guide/writingTheApp.html | 92 +-
.../3/guide/buildingApi.html | 74 +-
guides/using-hal-with-json-views/3/guide/hal.html | 549 +++++++++-
.../using-hal-with-json-views/3/guide/index.html | 630 ++++++++++-
.../3/guide/pages/customizingOurApi.html | 74 +-
.../3/guide/pages/embedded.html | 64 +-
.../3/guide/pages/links.html | 66 +-
.../3/guide/pages/mimeTypes.html | 55 +-
.../3/guide/pages/pagination.html | 240 ++++-
.../3/guide/pages/templates.html | 124 ++-
.../3/guide/runningTheApp.html | 7 +-
.../using-hal-with-json-views/3/guide/single.html | 630 ++++++++++-
.../using-hal-with-json-views/3/img/jsonviewer.png | Bin 0 -> 363312 bytes
guides/using-hal-with-json-views/3/img/postman.png | Bin 0 -> 551764 bytes
.../4/guide/buildingApi.html | 78 +-
guides/using-hal-with-json-views/4/guide/hal.html | 629 ++++++++++-
.../using-hal-with-json-views/4/guide/index.html | 714 ++++++++++++-
.../4/guide/pages/customizingOurApi.html | 78 +-
.../4/guide/pages/embedded.html | 136 ++-
.../4/guide/pages/links.html | 70 +-
.../4/guide/pages/mimeTypes.html | 55 +-
.../4/guide/pages/pagination.html | 240 ++++-
.../4/guide/pages/templates.html | 128 ++-
.../4/guide/runningTheApp.html | 7 +-
.../using-hal-with-json-views/4/guide/single.html | 714 ++++++++++++-
.../using-hal-with-json-views/4/img/jsonviewer.png | Bin 0 -> 363312 bytes
guides/using-hal-with-json-views/4/img/postman.png | Bin 0 -> 551764 bytes
guides/using-the-vue-profile/3/img/vue.png | Bin 0 -> 6849 bytes
guides/using-the-vue-profile/4/img/vue.png | Bin 0 -> 6849 bytes
guides/vaadin-grails/4/guide/index.html | 559 +++++++++-
.../4/guide/pages/addingToVaadin.html | 48 +-
guides/vaadin-grails/4/guide/pages/addingView.html | 289 ++++-
.../4/guide/pages/creatingTestData.html | 27 +-
.../4/guide/pages/creatingTheControllers.html | 17 +-
.../4/guide/pages/creatingTheDomain.html | 109 +-
.../4/guide/pages/creatingTheServices.html | 69 +-
guides/vaadin-grails/4/guide/single.html | 559 +++++++++-
guides/vaadin-grails/4/guide/writingTheApp.html | 559 +++++++++-
.../4/img/charts-on-mobile-and-desktop.png | Bin 0 -> 152471 bytes
guides/vaadin-grails/4/img/grailsDefault.png | Bin 0 -> 75957 bytes
guides/vaadin-grails/4/img/runningGarageApp.png | Bin 0 -> 60552 bytes
guides/vaadin-grails/4/img/vaadinDefault.png | Bin 0 -> 32068 bytes
rss.xml | 2 +-
1084 files changed, 99180 insertions(+), 6577 deletions(-)
diff --git a/guides/adding-commit-info/3/guide/configuration.html b/guides/adding-commit-info/3/guide/configuration.html
index 40eb21ff7bd..2656ccca8c3 100644
--- a/guides/adding-commit-info/3/guide/configuration.html
+++ b/guides/adding-commit-info/3/guide/configuration.html
@@ -148,7 +148,10 @@ You can display the full git properties via configuration by setting <code>manag
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">full</span></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -207,13 +210,43 @@ The <a href="https://github.com/spring-projects/spring-boot/blob/master/spring-b
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> testCompile <span class="string"><span class="delimiter">"</span><span class="content">org.grails:grails-datastore-rest-client</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">/complete/src/integration-test/groovy/demo/InfoSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/demo/InfoSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.plugins.rest.client.RestBuilder</span>
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.transaction.Rollback</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">InfoSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test git commit info appears in JSON</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ RestBuilder rest = <span class="keyword">new</span> RestBuilder()
+
+ <span class="key">when</span>:
+ <span class="keyword">def</span> resp = rest.get(<span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">${</span>serverPort<span class="inline-delimiter">}</span></span><span class="content">/info</span><span class="delimiter">"</span></span>) {
+ header(<span class="string"><span class="delimiter">"</span><span class="content">Accept</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">application/json</span><span class="delimiter">"</span></span>)
+ }
+
+ <span class="key">then</span>:
+ resp.statusCode.value() == <span class="integer">200</span>
+ resp.json
+ resp.json.git
+ resp.json.git.commit
+ resp.json.git.commit.message
+ resp.json.git.commit.time
+ resp.json.git.commit.id
+ resp.json.git.commit.user
+ resp.json.git.branch
+ }
+}</code></pre>
</div>
</div>
@@ -239,8 +272,12 @@ For example if you only wanted simple mode enabled in production you might use t
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span>
-<span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">environments</span>:
+ <span class="key">production</span>:
+ <span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">simple</span></span></code></pre>
</div>
</div>
diff --git a/guides/adding-commit-info/3/guide/index.html b/guides/adding-commit-info/3/guide/index.html
index 226f537c5ab..9173334c606 100644
--- a/guides/adding-commit-info/3/guide/index.html
+++ b/guides/adding-commit-info/3/guide/index.html
@@ -457,10 +457,15 @@ The goal of this plugin is to capture the commit information and store it in a f
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy">buildscript {
+ repositories {
+ maven { url <span class="string"><span class="delimiter">"</span><span class="content">https://plugins.gradle.org/m2/</span><span class="delimiter">"</span></span> }
+ }
+ dependencies {
+ classpath <span class="string"><span class="delimiter">"</span><span class="content">gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:1.4.17</span><span class="delimiter">"</span></span>
+ }
+}
+apply <span class="key">plugin</span>: <span class="string"><span class="delimiter">"</span><span class="content">com.gorylenko.gradle-git-properties</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<hr>
@@ -552,7 +557,7 @@ The endpoints are disabled by default so the first step is to enabled them.</p>
<div class="listingblock">
<div class="title">grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/../initial/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">// missing snippet</span>: <span class="string"><span class="content">../initial/grails-app/conf/application.yml</span></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -659,7 +664,10 @@ You can display the full git properties via configuration by setting <code>manag
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">full</span></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -718,13 +726,43 @@ The <a href="https://github.com/spring-projects/spring-boot/blob/master/spring-b
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> testCompile <span class="string"><span class="delimiter">"</span><span class="content">org.grails:grails-datastore-rest-client</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">/complete/src/integration-test/groovy/demo/InfoSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/demo/InfoSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.plugins.rest.client.RestBuilder</span>
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.transaction.Rollback</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">InfoSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test git commit info appears in JSON</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ RestBuilder rest = <span class="keyword">new</span> RestBuilder()
+
+ <span class="key">when</span>:
+ <span class="keyword">def</span> resp = rest.get(<span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">${</span>serverPort<span class="inline-delimiter">}</span></span><span class="content">/info</span><span class="delimiter">"</span></span>) {
+ header(<span class="string"><span class="delimiter">"</span><span class="content">Accept</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">application/json</span><span class="delimiter">"</span></span>)
+ }
+
+ <span class="key">then</span>:
+ resp.statusCode.value() == <span class="integer">200</span>
+ resp.json
+ resp.json.git
+ resp.json.git.commit
+ resp.json.git.commit.message
+ resp.json.git.commit.time
+ resp.json.git.commit.id
+ resp.json.git.commit.user
+ resp.json.git.branch
+ }
+}</code></pre>
</div>
</div>
@@ -750,8 +788,12 @@ For example if you only wanted simple mode enabled in production you might use t
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span>
-<span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">environments</span>:
+ <span class="key">production</span>:
+ <span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">simple</span></span></code></pre>
</div>
</div>
diff --git a/guides/adding-commit-info/3/guide/pages/actuator.html b/guides/adding-commit-info/3/guide/pages/actuator.html
index c222f83f0b7..4852a3b3cb1 100644
--- a/guides/adding-commit-info/3/guide/pages/actuator.html
+++ b/guides/adding-commit-info/3/guide/pages/actuator.html
@@ -114,7 +114,7 @@ The endpoints are disabled by default so the first step is to enabled them.</p>
<div class="listingblock">
<div class="title">grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/../initial/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">// missing snippet</span>: <span class="string"><span class="content">../initial/grails-app/conf/application.yml</span></span></code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/adding-commit-info/3/guide/pages/configuringGitInfoContributor.html b/guides/adding-commit-info/3/guide/pages/configuringGitInfoContributor.html
index 27b6ae1b761..acfd32f44a0 100644
--- a/guides/adding-commit-info/3/guide/pages/configuringGitInfoContributor.html
+++ b/guides/adding-commit-info/3/guide/pages/configuringGitInfoContributor.html
@@ -97,7 +97,10 @@ You can display the full git properties via configuration by setting <code>manag
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">full</span></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -156,13 +159,43 @@ The <a href="https://github.com/spring-projects/spring-boot/blob/master/spring-b
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> testCompile <span class="string"><span class="delimiter">"</span><span class="content">org.grails:grails-datastore-rest-client</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">/complete/src/integration-test/groovy/demo/InfoSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/demo/InfoSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.plugins.rest.client.RestBuilder</span>
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.transaction.Rollback</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">InfoSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test git commit info appears in JSON</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ RestBuilder rest = <span class="keyword">new</span> RestBuilder()
+
+ <span class="key">when</span>:
+ <span class="keyword">def</span> resp = rest.get(<span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">${</span>serverPort<span class="inline-delimiter">}</span></span><span class="content">/info</span><span class="delimiter">"</span></span>) {
+ header(<span class="string"><span class="delimiter">"</span><span class="content">Accept</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">application/json</span><span class="delimiter">"</span></span>)
+ }
+
+ <span class="key">then</span>:
+ resp.statusCode.value() == <span class="integer">200</span>
+ resp.json
+ resp.json.git
+ resp.json.git.commit
+ resp.json.git.commit.message
+ resp.json.git.commit.time
+ resp.json.git.commit.id
+ resp.json.git.commit.user
+ resp.json.git.branch
+ }
+}</code></pre>
</div>
</div>
diff --git a/guides/adding-commit-info/3/guide/pages/gradlePlugin.html b/guides/adding-commit-info/3/guide/pages/gradlePlugin.html
index f7de91617c9..f5b882b1f45 100644
--- a/guides/adding-commit-info/3/guide/pages/gradlePlugin.html
+++ b/guides/adding-commit-info/3/guide/pages/gradlePlugin.html
@@ -100,10 +100,15 @@ The goal of this plugin is to capture the commit information and store it in a f
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy">buildscript {
+ repositories {
+ maven { url <span class="string"><span class="delimiter">"</span><span class="content">https://plugins.gradle.org/m2/</span><span class="delimiter">"</span></span> }
+ }
+ dependencies {
+ classpath <span class="string"><span class="delimiter">"</span><span class="content">gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:1.4.17</span><span class="delimiter">"</span></span>
+ }
+}
+apply <span class="key">plugin</span>: <span class="string"><span class="delimiter">"</span><span class="content">com.gorylenko.gradle-git-properties</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<hr>
diff --git a/guides/adding-commit-info/3/guide/pages/perEnvironmentConfig.html b/guides/adding-commit-info/3/guide/pages/perEnvironmentConfig.html
index 886cbe1d747..33d94c0d1fa 100644
--- a/guides/adding-commit-info/3/guide/pages/perEnvironmentConfig.html
+++ b/guides/adding-commit-info/3/guide/pages/perEnvironmentConfig.html
@@ -99,8 +99,12 @@ For example if you only wanted simple mode enabled in production you might use t
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span>
-<span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">environments</span>:
+ <span class="key">production</span>:
+ <span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">simple</span></span></code></pre>
</div>
</div>
diff --git a/guides/adding-commit-info/3/guide/single.html b/guides/adding-commit-info/3/guide/single.html
index 226f537c5ab..9173334c606 100644
--- a/guides/adding-commit-info/3/guide/single.html
+++ b/guides/adding-commit-info/3/guide/single.html
@@ -457,10 +457,15 @@ The goal of this plugin is to capture the commit information and store it in a f
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy">buildscript {
+ repositories {
+ maven { url <span class="string"><span class="delimiter">"</span><span class="content">https://plugins.gradle.org/m2/</span><span class="delimiter">"</span></span> }
+ }
+ dependencies {
+ classpath <span class="string"><span class="delimiter">"</span><span class="content">gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:1.4.17</span><span class="delimiter">"</span></span>
+ }
+}
+apply <span class="key">plugin</span>: <span class="string"><span class="delimiter">"</span><span class="content">com.gorylenko.gradle-git-properties</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<hr>
@@ -552,7 +557,7 @@ The endpoints are disabled by default so the first step is to enabled them.</p>
<div class="listingblock">
<div class="title">grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/../initial/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">// missing snippet</span>: <span class="string"><span class="content">../initial/grails-app/conf/application.yml</span></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -659,7 +664,10 @@ You can display the full git properties via configuration by setting <code>manag
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">full</span></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -718,13 +726,43 @@ The <a href="https://github.com/spring-projects/spring-boot/blob/master/spring-b
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> testCompile <span class="string"><span class="delimiter">"</span><span class="content">org.grails:grails-datastore-rest-client</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">/complete/src/integration-test/groovy/demo/InfoSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/demo/InfoSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.plugins.rest.client.RestBuilder</span>
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.transaction.Rollback</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">InfoSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test git commit info appears in JSON</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ RestBuilder rest = <span class="keyword">new</span> RestBuilder()
+
+ <span class="key">when</span>:
+ <span class="keyword">def</span> resp = rest.get(<span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">${</span>serverPort<span class="inline-delimiter">}</span></span><span class="content">/info</span><span class="delimiter">"</span></span>) {
+ header(<span class="string"><span class="delimiter">"</span><span class="content">Accept</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">application/json</span><span class="delimiter">"</span></span>)
+ }
+
+ <span class="key">then</span>:
+ resp.statusCode.value() == <span class="integer">200</span>
+ resp.json
+ resp.json.git
+ resp.json.git.commit
+ resp.json.git.commit.message
+ resp.json.git.commit.time
+ resp.json.git.commit.id
+ resp.json.git.commit.user
+ resp.json.git.branch
+ }
+}</code></pre>
</div>
</div>
@@ -750,8 +788,12 @@ For example if you only wanted simple mode enabled in production you might use t
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span>
-<span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">environments</span>:
+ <span class="key">production</span>:
+ <span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">simple</span></span></code></pre>
</div>
</div>
diff --git a/guides/adding-commit-info/3/guide/writingTheApp.html b/guides/adding-commit-info/3/guide/writingTheApp.html
index 9a1e6e3359f..a04ecc7ab0f 100644
--- a/guides/adding-commit-info/3/guide/writingTheApp.html
+++ b/guides/adding-commit-info/3/guide/writingTheApp.html
@@ -138,10 +138,15 @@ The goal of this plugin is to capture the commit information and store it in a f
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy">buildscript {
+ repositories {
+ maven { url <span class="string"><span class="delimiter">"</span><span class="content">https://plugins.gradle.org/m2/</span><span class="delimiter">"</span></span> }
+ }
+ dependencies {
+ classpath <span class="string"><span class="delimiter">"</span><span class="content">gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:1.4.17</span><span class="delimiter">"</span></span>
+ }
+}
+apply <span class="key">plugin</span>: <span class="string"><span class="delimiter">"</span><span class="content">com.gorylenko.gradle-git-properties</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<hr>
@@ -233,7 +238,7 @@ The endpoints are disabled by default so the first step is to enabled them.</p>
<div class="listingblock">
<div class="title">grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/../initial/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">// missing snippet</span>: <span class="string"><span class="content">../initial/grails-app/conf/application.yml</span></span></code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/adding-commit-info/4/guide/configuration.html b/guides/adding-commit-info/4/guide/configuration.html
index 12dab0e60df..d4ab8308174 100644
--- a/guides/adding-commit-info/4/guide/configuration.html
+++ b/guides/adding-commit-info/4/guide/configuration.html
@@ -148,7 +148,17 @@ You can display the full git properties via configuration by setting <code>manag
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">management</span>:
+ <span class="key">endpoints</span>:
+ <span class="key">enabled-by-default</span>: <span class="string"><span class="content">true</span></span>
+<span class="comment"># info:</span>
+<span class="comment"># enabled: true</span>
+<span class="comment"># info:</span>
+<span class="comment"># git:</span>
+<span class="comment"># mode: full</span>
+<span class="comment"># endpoint:</span>
+<span class="comment"># jmx:</span>
+<span class="comment"># enabled: true</span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -207,13 +217,48 @@ The <a href="https://github.com/spring-projects/spring-boot/blob/master/spring-b
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> testCompile <span class="string"><span class="delimiter">"</span><span class="content">io.micronaut:micronaut-http-client:1.0.2</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">/complete/src/integration-test/groovy/demo/InfoSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/demo/InfoSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">InfoSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test git commit info appears in JSON</span><span class="delimiter">"</span></span>() {
+ <span class="key">when</span>:
+ HttpResponse<<span class="predefined-type">Map</span>> resp = client.toBlocking().exchange(HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/actuator/info</span><span class="delimiter">"</span></span>), <span class="predefined-type">Map</span>)
+
+ <span class="key">then</span>:
+ resp.status == HttpStatus.OK
+ resp.body()
+ resp.body().git
+ resp.body().git.commit.time
+ resp.body().git.commit.id
+ resp.body().git.branch
+ }
+}</code></pre>
</div>
</div>
@@ -239,8 +284,12 @@ For example if you only wanted simple mode enabled in production you might use t
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span>
-<span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">environments</span>:
+ <span class="key">production</span>:
+ <span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">simple</span></span></code></pre>
</div>
</div>
diff --git a/guides/adding-commit-info/4/guide/index.html b/guides/adding-commit-info/4/guide/index.html
index e4018bff2e8..f77be79e2cd 100644
--- a/guides/adding-commit-info/4/guide/index.html
+++ b/guides/adding-commit-info/4/guide/index.html
@@ -457,10 +457,15 @@ The goal of this plugin is to capture the commit information and store it in a f
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy">buildscript {
+ repositories {
+ maven { url <span class="string"><span class="delimiter">"</span><span class="content">https://plugins.gradle.org/m2/</span><span class="delimiter">"</span></span> }
+ }
+ dependencies {
+ classpath <span class="string"><span class="delimiter">"</span><span class="content">gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:2.0.0</span><span class="delimiter">"</span></span>
+ }
+}
+apply <span class="key">plugin</span>: <span class="string"><span class="delimiter">"</span><span class="content">com.gorylenko.gradle-git-properties</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<hr>
@@ -552,7 +557,7 @@ The endpoints are disabled by default so the first step is to enabled them.</p>
<div class="listingblock">
<div class="title">grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/../initial/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">// missing snippet</span>: <span class="string"><span class="content">../initial/grails-app/conf/application.yml</span></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -659,7 +664,17 @@ You can display the full git properties via configuration by setting <code>manag
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">management</span>:
+ <span class="key">endpoints</span>:
+ <span class="key">enabled-by-default</span>: <span class="string"><span class="content">true</span></span>
+<span class="comment"># info:</span>
+<span class="comment"># enabled: true</span>
+<span class="comment"># info:</span>
+<span class="comment"># git:</span>
+<span class="comment"># mode: full</span>
+<span class="comment"># endpoint:</span>
+<span class="comment"># jmx:</span>
+<span class="comment"># enabled: true</span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -718,13 +733,48 @@ The <a href="https://github.com/spring-projects/spring-boot/blob/master/spring-b
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> testCompile <span class="string"><span class="delimiter">"</span><span class="content">io.micronaut:micronaut-http-client:1.0.2</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">/complete/src/integration-test/groovy/demo/InfoSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/demo/InfoSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">InfoSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test git commit info appears in JSON</span><span class="delimiter">"</span></span>() {
+ <span class="key">when</span>:
+ HttpResponse<<span class="predefined-type">Map</span>> resp = client.toBlocking().exchange(HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/actuator/info</span><span class="delimiter">"</span></span>), <span class="predefined-type">Map</span>)
+
+ <span class="key">then</span>:
+ resp.status == HttpStatus.OK
+ resp.body()
+ resp.body().git
+ resp.body().git.commit.time
+ resp.body().git.commit.id
+ resp.body().git.branch
+ }
+}</code></pre>
</div>
</div>
@@ -750,8 +800,12 @@ For example if you only wanted simple mode enabled in production you might use t
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span>
-<span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">environments</span>:
+ <span class="key">production</span>:
+ <span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">simple</span></span></code></pre>
</div>
</div>
diff --git a/guides/adding-commit-info/4/guide/pages/actuator.html b/guides/adding-commit-info/4/guide/pages/actuator.html
index df310cd2008..8dc40e51a46 100644
--- a/guides/adding-commit-info/4/guide/pages/actuator.html
+++ b/guides/adding-commit-info/4/guide/pages/actuator.html
@@ -114,7 +114,7 @@ The endpoints are disabled by default so the first step is to enabled them.</p>
<div class="listingblock">
<div class="title">grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/../initial/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">// missing snippet</span>: <span class="string"><span class="content">../initial/grails-app/conf/application.yml</span></span></code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/adding-commit-info/4/guide/pages/configuringGitInfoContributor.html b/guides/adding-commit-info/4/guide/pages/configuringGitInfoContributor.html
index 5e26f44e854..bb6039996b1 100644
--- a/guides/adding-commit-info/4/guide/pages/configuringGitInfoContributor.html
+++ b/guides/adding-commit-info/4/guide/pages/configuringGitInfoContributor.html
@@ -97,7 +97,17 @@ You can display the full git properties via configuration by setting <code>manag
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">management</span>:
+ <span class="key">endpoints</span>:
+ <span class="key">enabled-by-default</span>: <span class="string"><span class="content">true</span></span>
+<span class="comment"># info:</span>
+<span class="comment"># enabled: true</span>
+<span class="comment"># info:</span>
+<span class="comment"># git:</span>
+<span class="comment"># mode: full</span>
+<span class="comment"># endpoint:</span>
+<span class="comment"># jmx:</span>
+<span class="comment"># enabled: true</span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -156,13 +166,48 @@ The <a href="https://github.com/spring-projects/spring-boot/blob/master/spring-b
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> testCompile <span class="string"><span class="delimiter">"</span><span class="content">io.micronaut:micronaut-http-client:1.0.2</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">/complete/src/integration-test/groovy/demo/InfoSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/demo/InfoSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">InfoSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test git commit info appears in JSON</span><span class="delimiter">"</span></span>() {
+ <span class="key">when</span>:
+ HttpResponse<<span class="predefined-type">Map</span>> resp = client.toBlocking().exchange(HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/actuator/info</span><span class="delimiter">"</span></span>), <span class="predefined-type">Map</span>)
+
+ <span class="key">then</span>:
+ resp.status == HttpStatus.OK
+ resp.body()
+ resp.body().git
+ resp.body().git.commit.time
+ resp.body().git.commit.id
+ resp.body().git.branch
+ }
+}</code></pre>
</div>
</div>
diff --git a/guides/adding-commit-info/4/guide/pages/gradlePlugin.html b/guides/adding-commit-info/4/guide/pages/gradlePlugin.html
index 637a80e404c..e211b405c47 100644
--- a/guides/adding-commit-info/4/guide/pages/gradlePlugin.html
+++ b/guides/adding-commit-info/4/guide/pages/gradlePlugin.html
@@ -100,10 +100,15 @@ The goal of this plugin is to capture the commit information and store it in a f
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy">buildscript {
+ repositories {
+ maven { url <span class="string"><span class="delimiter">"</span><span class="content">https://plugins.gradle.org/m2/</span><span class="delimiter">"</span></span> }
+ }
+ dependencies {
+ classpath <span class="string"><span class="delimiter">"</span><span class="content">gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:2.0.0</span><span class="delimiter">"</span></span>
+ }
+}
+apply <span class="key">plugin</span>: <span class="string"><span class="delimiter">"</span><span class="content">com.gorylenko.gradle-git-properties</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<hr>
diff --git a/guides/adding-commit-info/4/guide/pages/perEnvironmentConfig.html b/guides/adding-commit-info/4/guide/pages/perEnvironmentConfig.html
index 4c87c8b88c6..cd93af19656 100644
--- a/guides/adding-commit-info/4/guide/pages/perEnvironmentConfig.html
+++ b/guides/adding-commit-info/4/guide/pages/perEnvironmentConfig.html
@@ -99,8 +99,12 @@ For example if you only wanted simple mode enabled in production you might use t
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span>
-<span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">environments</span>:
+ <span class="key">production</span>:
+ <span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">simple</span></span></code></pre>
</div>
</div>
diff --git a/guides/adding-commit-info/4/guide/single.html b/guides/adding-commit-info/4/guide/single.html
index e4018bff2e8..f77be79e2cd 100644
--- a/guides/adding-commit-info/4/guide/single.html
+++ b/guides/adding-commit-info/4/guide/single.html
@@ -457,10 +457,15 @@ The goal of this plugin is to capture the commit information and store it in a f
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy">buildscript {
+ repositories {
+ maven { url <span class="string"><span class="delimiter">"</span><span class="content">https://plugins.gradle.org/m2/</span><span class="delimiter">"</span></span> }
+ }
+ dependencies {
+ classpath <span class="string"><span class="delimiter">"</span><span class="content">gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:2.0.0</span><span class="delimiter">"</span></span>
+ }
+}
+apply <span class="key">plugin</span>: <span class="string"><span class="delimiter">"</span><span class="content">com.gorylenko.gradle-git-properties</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<hr>
@@ -552,7 +557,7 @@ The endpoints are disabled by default so the first step is to enabled them.</p>
<div class="listingblock">
<div class="title">grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/../initial/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">// missing snippet</span>: <span class="string"><span class="content">../initial/grails-app/conf/application.yml</span></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -659,7 +664,17 @@ You can display the full git properties via configuration by setting <code>manag
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">management</span>:
+ <span class="key">endpoints</span>:
+ <span class="key">enabled-by-default</span>: <span class="string"><span class="content">true</span></span>
+<span class="comment"># info:</span>
+<span class="comment"># enabled: true</span>
+<span class="comment"># info:</span>
+<span class="comment"># git:</span>
+<span class="comment"># mode: full</span>
+<span class="comment"># endpoint:</span>
+<span class="comment"># jmx:</span>
+<span class="comment"># enabled: true</span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -718,13 +733,48 @@ The <a href="https://github.com/spring-projects/spring-boot/blob/master/spring-b
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> testCompile <span class="string"><span class="delimiter">"</span><span class="content">io.micronaut:micronaut-http-client:1.0.2</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">/complete/src/integration-test/groovy/demo/InfoSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/demo/InfoSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">InfoSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test git commit info appears in JSON</span><span class="delimiter">"</span></span>() {
+ <span class="key">when</span>:
+ HttpResponse<<span class="predefined-type">Map</span>> resp = client.toBlocking().exchange(HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/actuator/info</span><span class="delimiter">"</span></span>), <span class="predefined-type">Map</span>)
+
+ <span class="key">then</span>:
+ resp.status == HttpStatus.OK
+ resp.body()
+ resp.body().git
+ resp.body().git.commit.time
+ resp.body().git.commit.id
+ resp.body().git.branch
+ }
+}</code></pre>
</div>
</div>
@@ -750,8 +800,12 @@ For example if you only wanted simple mode enabled in production you might use t
<div class="listingblock">
<div class="title">/complete/grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span>
-<span class="error">link:../snippets/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">environments</span>:
+ <span class="key">production</span>:
+ <span class="key">management</span>:
+ <span class="key">info</span>:
+ <span class="key">git</span>:
+ <span class="key">mode</span>: <span class="string"><span class="content">simple</span></span></code></pre>
</div>
</div>
diff --git a/guides/adding-commit-info/4/guide/writingTheApp.html b/guides/adding-commit-info/4/guide/writingTheApp.html
index 9f58c850597..e9a3613e6e1 100644
--- a/guides/adding-commit-info/4/guide/writingTheApp.html
+++ b/guides/adding-commit-info/4/guide/writingTheApp.html
@@ -138,10 +138,15 @@ The goal of this plugin is to capture the commit information and store it in a f
<div class="listingblock">
<div class="title">/complete/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]
-<span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy">buildscript {
+ repositories {
+ maven { url <span class="string"><span class="delimiter">"</span><span class="content">https://plugins.gradle.org/m2/</span><span class="delimiter">"</span></span> }
+ }
+ dependencies {
+ classpath <span class="string"><span class="delimiter">"</span><span class="content">gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:2.0.0</span><span class="delimiter">"</span></span>
+ }
+}
+apply <span class="key">plugin</span>: <span class="string"><span class="delimiter">"</span><span class="content">com.gorylenko.gradle-git-properties</span><span class="delimiter">"</span></span></code></pre>
</div>
</div>
<hr>
@@ -233,7 +238,7 @@ The endpoints are disabled by default so the first step is to enabled them.</p>
<div class="listingblock">
<div class="title">grails-app/conf/application.yml</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="yaml"><span class="error">link:../snippets/../initial/grails-app/conf/application.yml[role=include]</span></code></pre>
+<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">// missing snippet</span>: <span class="string"><span class="content">../initial/grails-app/conf/application.yml</span></span></code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/building-a-react-app/4/guide/index.html b/guides/building-a-react-app/4/guide/index.html
index e832aeafdb9..cfe0e5df683 100644
--- a/guides/building-a-react-app/4/guide/index.html
+++ b/guides/building-a-react-app/4/guide/index.html
@@ -305,25 +305,77 @@ $ grails create-domain-class demo.Model</code></pre>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Vehicle.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Vehicle.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/vehicle</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Vehicle</span> {
+
+ <span class="predefined-type">String</span> name
+
+ Make make
+ Model model
+
+ <span class="directive">static</span> belongsTo = [<span class="key">driver</span>: <span class="predefined-type">Driver</span>]
+
+ <span class="directive">static</span> constraints = {
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Driver.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/<span class="predefined-type">Driver</span>.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/driver</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Driver</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> hasMany = [ <span class="key">vehicles</span>: Vehicle ]
+
+ <span class="directive">static</span> constraints = {
+ vehicles <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Make.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Make.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/make</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Make</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> constraints = {
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Model.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Model.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/model</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Model</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> constraints = {
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -332,7 +384,35 @@ $ grails create-domain-class demo.Model</code></pre>
<div class="listingblock">
<div class="title">grails-app/init/demo/BootStrap.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/init/demo/BootStrap.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">demo.Driver</span>
+<span class="keyword">import</span> <span class="include">demo.Make</span>
+<span class="keyword">import</span> <span class="include">demo.Model</span>
+<span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+<span class="type">class</span> <span class="class">BootStrap</span> {
+
+ <span class="keyword">def</span> init = { servletContext ->
+ log.info <span class="string"><span class="delimiter">"</span><span class="content">Loading database...</span><span class="delimiter">"</span></span>
+ <span class="keyword">def</span> driver1 = <span class="keyword">new</span> <span class="predefined-type">Driver</span>(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Susan</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> driver2 = <span class="keyword">new</span> <span class="predefined-type">Driver</span>(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Pedro</span><span class="delimiter">"</span></span>).save()
+
+ <span class="keyword">def</span> nissan = <span class="keyword">new</span> Make(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Nissan</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> ford = <span class="keyword">new</span> Make(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Ford</span><span class="delimiter">"</span></span>).save()
+
+ <span class="keyword">def</span> titan = <span class="keyword">new</span> Model(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Titan</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> leaf = <span class="keyword">new</span> Model(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Leaf</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> windstar = <span class="keyword">new</span> Model(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Windstar</span><span class="delimiter">"</span></span>).save()
+
+ <span class="keyword">new</span> Vehicle(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Pickup</span><span class="delimiter">"</span></span>, <span class="key">driver</span>: driver1, <span class="key">make</span>: nissan, <span class="key">model</span>: titan).save()
+ <span class="keyword">new</span> Vehicle(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Economy</span><span class="delimiter">"</span></span>, <span class="key">driver</span>: driver1, <span class="key">make</span>: nissan, <span class="key">model</span>: leaf).save()
+ <span class="keyword">new</span> Vehicle(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Minivan</span><span class="delimiter">"</span></span>, <span class="key">driver</span>: driver2, <span class="key">make</span>: ford, <span class="key">model</span>: windstar).save()
+
+ }
+ <span class="keyword">def</span> destroy = {
+ }
+}</code></pre>
</div>
</div>
@@ -481,7 +561,25 @@ Connection: close
<div class="listingblock">
<div class="title">grails-app/views/vehicle/_vehicle.gson</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/views/vehicle/_vehicle.gson[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+model {
+ Vehicle vehicle
+}
+json {
+ id vehicle.id
+
+ name vehicle.name
+
+ make <span class="key">name</span>: vehicle.make.name,
+ <span class="key">id</span>: vehicle.make.id
+
+ model <span class="key">name</span>: vehicle.model.name,
+ <span class="key">id</span>: vehicle.model.id
+
+ driver <span class="key">name</span>: vehicle.driver.name,
+ <span class="key">id</span>: vehicle.driver.id
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -535,7 +633,51 @@ Connection: close
</div>
<div class="listingblock">
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/main/webapp/app/Vehicles.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> React from <span class="string"><span class="delimiter">'</span><span class="content">react</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {Table} from <span class="string"><span class="delimiter">'</span><span class="content">react-bootstrap</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {array} from <span class="string"><span class="delimiter">'</span><span class="content">prop-types</span><span class="delimiter">'</span></span>;
+
+<span class="reserved">class</span> Vehicles <span class="reserved">extends</span> React.Component {
+
+ render() {
+ <span class="keyword">function</span> <span class="function">renderVehicleRow</span>(vehicle) {
+
+ <span class="keyword">return</span> (<tr key={vehicle.id}>
+ <span class="tag"><td></span>{vehicle.id}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.make.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.model.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.driver.name}<span class="tag"></td></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">tr>);
+ }
+
+
+ return <div>
+ <Table striped bordered condensed hover>
+ <thead>
+ <tr>
+ <th>ID<</span><span class="delimiter">/</span></span>th>
+ <span class="tag"><th></span>Name<span class="tag"></th></span>
+ <span class="tag"><th></span>Make<span class="tag"></th></span>
+ <span class="tag"><th></span>Model<span class="tag"></th></span>
+ <span class="tag"><th></span>Driver<span class="tag"></th></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">tr>
+ <</span><span class="delimiter">/</span></span>thead>
+ <span class="tag"><tbody></span>
+
+ {this.props.vehicles.map(renderVehicleRow)}
+
+ <span class="tag"></tbody></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">Table>
+ <</span><span class="delimiter">/</span></span>div>;
+ }
+}
+
+Vehicles.propTypes = {
+ <span class="key">vehicles</span>: array
+};
+
+<span class="reserved">export</span> <span class="keyword">default</span> Vehicles;</code></pre>
</div>
</div>
<div class="paragraph">
@@ -624,7 +766,13 @@ ReactDOM.render(<Garage />, document.getElementById(<span class="string"><
<div class="listingblock">
<div class="title">webpack.config.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>webpack.config.js[role=include]
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="keyword">var</span> path = require(<span class="string"><span class="delimiter">'</span><span class="content">path</span><span class="delimiter">'</span></span>);
+
+module.exports = {
+ <span class="key">entry</span>: {
+ <span class="key">index</span>: <span class="string"><span class="delimiter">'</span><span class="content">./src/main/webapp/index.js</span><span class="delimiter">'</span></span>, <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">garage</span>: <span class="string"><span class="delimiter">'</span><span class="content">./src/main/webapp/app/Garage.js</span><span class="delimiter">'</span></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ },
<span class="comment">//...</span></code></pre>
</div>
</div>
@@ -650,7 +798,11 @@ ReactDOM.render(<Garage />, document.getElementById(<span class="string"><
<div class="title">webpack.config.js</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="javascript"><span class="comment">//...</span>
-link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>webpack.config.js[role=include]
+output: {
+ <span class="key">path</span>: path.join(__dirname, <span class="string"><span class="delimiter">'</span><span class="content">grails-app/assets/javascripts</span><span class="delimiter">'</span></span>),
+ <span class="key">publicPath</span>: <span class="string"><span class="delimiter">'</span><span class="content">/assets/</span><span class="delimiter">'</span></span>,
+ <span class="key">filename</span>: <span class="string"><span class="delimiter">'</span><span class="content">bundle-[name].js</span><span class="delimiter">'</span></span>
+},
<span class="comment">//...</span></code></pre>
</div>
</div>
@@ -710,7 +862,11 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">grails-app/controllers/demo/GarageController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/GarageController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="type">class</span> <span class="class">GarageController</span> {
+ <span class="keyword">def</span> <span class="function">index</span>() { }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -719,7 +875,24 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">grails-app/views/garage/index.gsp</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="xml">link:../snippets/grails-app/views/garage/index.gsp[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="xml"><span class="doctype"><!doctype html></span>
+<span class="tag"><html></span>
+<span class="tag"><head></span>
+ <span class="tag"><meta</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">layout</span><span class="delimiter">"</span></span> <span class="attribute-name">content</span>=<span class="string"><span class="delimiter">"</span><span class="content">main</span><span class="delimiter">"</span></span><span class="tag">/></span>
+ <span class="tag"><title></span>Garage<span class="tag"></title></span>
+ <span class="tag"><asset:link</span> <span class="attribute-name">rel</span>=<span class="string"><span class="delimiter">"</span><span class="content">icon</span><span class="delimiter">"</span></span> <span class="attribute-name">href</span>=<span class="string"><span class="delimiter">"</span><span class="content">favicon.ico</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">&q [...]
+<span class="tag"></head></span>
+<span class="tag"><body></span>
+
+<span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">content</span><span class="delimiter">"</span></span> <span class="attribute-name">role</span>=<span class="string"><span class="delimiter">"</span><span class="content">main</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><section</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row colset-2-its</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">garage</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"><asset:javascript</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">bundle-garage.js</span><span class="delimiter">"</span></span> <span class="tag">/></span>
+ <span class="tag"></section></span>
+<span class="tag"></div></span>
+
+<span class="tag"></body></span>
+<span class="tag"></html></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -809,7 +982,100 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">src/main/webapp/app/AddVehicleForm.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/main/webapp/app/AddVehicleForm.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> React from <span class="string"><span class="delimiter">'</span><span class="content">react</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {array, func} from <span class="string"><span class="delimiter">'</span><span class="content">prop-types</span><span class="delimiter">'</span></span>;
+
+<span class="reserved">class</span> AddVehicleForm <span class="reserved">extends</span> React.Component {
+
+ constructor(props) {
+ <span class="reserved">super</span>(props);
+ <span class="local-variable">this</span>.state = { <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>,
+ <span class="key">make</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>},
+ <span class="key">model</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>},
+ <span class="key">driver</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>}};
+ }
+
+ handleSubmit = (event) => { <i class="conum" data-value="2"></i><b>(2)</b>
+ event.preventDefault();
+
+ const {name, make, model, driver} = <span class="local-variable">this</span>.state;
+
+ <span class="keyword">if</span> (!name || !make.id || !model.id || !driver.id) {
+ console.warn(<span class="string"><span class="delimiter">"</span><span class="content">missing required field!</span><span class="delimiter">"</span></span>);
+ <span class="keyword">return</span>;
+ }
+ <span class="local-variable">this</span>.props.onSubmit( {name, make, model, driver} ); <i class="conum" data-value="3"></i><b>(3)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>, <span class="key">make</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>}, <span class="key">model</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>} [...]
+ };
+
+ handleNameChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">name</span>: event.target.value });
+ };
+
+ handleMakeChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">make</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+ handleModelChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">model</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+ handleDriverChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">driver</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+
+ render() {
+
+ <span class="keyword">function</span> <span class="function">renderSelectList</span>(item) { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="keyword">return</span> <span class="tag"><option</span> <span class="attribute-name">key</span>=<span class="error">{</span><span class="attribute-value">item.id</span><span class="error">}</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">item.id</span><span class="error">}</span><span class="tag">></span>{item.name}<span class="tag"></option></span>
+ }
+
+ <span class="keyword">return</span>(
+ <span class="tag"><div></span>
+ <span class="tag"><h3></span>Add a Vehicle:<span class="tag"></h3></span>
+ <span class="tag"><form</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form form-inline</span><span class="delimiter">"</span></span> <span class="attribute-name">onSubmit</span>=<span class="error">{</span><span class="attribute-value">this.handleSubmit</span><span class="error">}</span> <span class="tag">></span>
+ <span class="tag"><label></span>Name<span class="tag"></label></span>
+ <span class="tag"><input</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delim [...]
+
+ <span class="tag"><label></span>Make<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">make</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span cla [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleMakeChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Make...<span class="tag"></option></span>
+ {this.props.makes.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><label></span>Model<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">model</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span cl [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleModelChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Model...<span class="tag"></option></span>
+ {this.props.models.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><label></span>Driver<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">driver</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span c [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleDriverChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Driver...<span class="tag"></option></span>
+ {this.props.drivers.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><input</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn btn-success</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">submit</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="string"><span class [...]
+ <span class="tag"></form></span>
+ <span class="tag"></div></span>
+ );
+
+ }
+}
+
+AddVehicleForm.propTypes = {
+ <span class="key">makes</span>: array,
+ <span class="key">models</span>: array,
+ <span class="key">drivers</span>: array,
+ <span class="key">onSubmit</span>: func
+};
+
+<span class="reserved">export</span> <span class="keyword">default</span> AddVehicleForm;</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-react-app/4/guide/pages/customizingApi.html b/guides/building-a-react-app/4/guide/pages/customizingApi.html
index 3e171661434..46456ed5f4c 100644
--- a/guides/building-a-react-app/4/guide/pages/customizingApi.html
+++ b/guides/building-a-react-app/4/guide/pages/customizingApi.html
@@ -124,7 +124,25 @@ Connection: close
<div class="listingblock">
<div class="title">grails-app/views/vehicle/_vehicle.gson</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/views/vehicle/_vehicle.gson[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+model {
+ Vehicle vehicle
+}
+json {
+ id vehicle.id
+
+ name vehicle.name
+
+ make <span class="key">name</span>: vehicle.make.name,
+ <span class="key">id</span>: vehicle.make.id
+
+ model <span class="key">name</span>: vehicle.model.name,
+ <span class="key">id</span>: vehicle.model.id
+
+ driver <span class="key">name</span>: vehicle.driver.name,
+ <span class="key">id</span>: vehicle.driver.id
+}</code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/building-a-react-app/4/guide/pages/post.html b/guides/building-a-react-app/4/guide/pages/post.html
index eb67e2d041f..55add53a6f6 100644
--- a/guides/building-a-react-app/4/guide/pages/post.html
+++ b/guides/building-a-react-app/4/guide/pages/post.html
@@ -96,7 +96,100 @@ function addJsClass() {
<div class="listingblock">
<div class="title">src/main/webapp/app/AddVehicleForm.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/main/webapp/app/AddVehicleForm.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> React from <span class="string"><span class="delimiter">'</span><span class="content">react</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {array, func} from <span class="string"><span class="delimiter">'</span><span class="content">prop-types</span><span class="delimiter">'</span></span>;
+
+<span class="reserved">class</span> AddVehicleForm <span class="reserved">extends</span> React.Component {
+
+ constructor(props) {
+ <span class="reserved">super</span>(props);
+ <span class="local-variable">this</span>.state = { <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>,
+ <span class="key">make</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>},
+ <span class="key">model</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>},
+ <span class="key">driver</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>}};
+ }
+
+ handleSubmit = (event) => { <i class="conum" data-value="2"></i><b>(2)</b>
+ event.preventDefault();
+
+ const {name, make, model, driver} = <span class="local-variable">this</span>.state;
+
+ <span class="keyword">if</span> (!name || !make.id || !model.id || !driver.id) {
+ console.warn(<span class="string"><span class="delimiter">"</span><span class="content">missing required field!</span><span class="delimiter">"</span></span>);
+ <span class="keyword">return</span>;
+ }
+ <span class="local-variable">this</span>.props.onSubmit( {name, make, model, driver} ); <i class="conum" data-value="3"></i><b>(3)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>, <span class="key">make</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>}, <span class="key">model</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>} [...]
+ };
+
+ handleNameChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">name</span>: event.target.value });
+ };
+
+ handleMakeChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">make</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+ handleModelChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">model</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+ handleDriverChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">driver</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+
+ render() {
+
+ <span class="keyword">function</span> <span class="function">renderSelectList</span>(item) { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="keyword">return</span> <span class="tag"><option</span> <span class="attribute-name">key</span>=<span class="error">{</span><span class="attribute-value">item.id</span><span class="error">}</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">item.id</span><span class="error">}</span><span class="tag">></span>{item.name}<span class="tag"></option></span>
+ }
+
+ <span class="keyword">return</span>(
+ <span class="tag"><div></span>
+ <span class="tag"><h3></span>Add a Vehicle:<span class="tag"></h3></span>
+ <span class="tag"><form</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form form-inline</span><span class="delimiter">"</span></span> <span class="attribute-name">onSubmit</span>=<span class="error">{</span><span class="attribute-value">this.handleSubmit</span><span class="error">}</span> <span class="tag">></span>
+ <span class="tag"><label></span>Name<span class="tag"></label></span>
+ <span class="tag"><input</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delim [...]
+
+ <span class="tag"><label></span>Make<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">make</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span cla [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleMakeChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Make...<span class="tag"></option></span>
+ {this.props.makes.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><label></span>Model<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">model</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span cl [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleModelChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Model...<span class="tag"></option></span>
+ {this.props.models.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><label></span>Driver<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">driver</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span c [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleDriverChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Driver...<span class="tag"></option></span>
+ {this.props.drivers.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><input</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn btn-success</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">submit</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="string"><span class [...]
+ <span class="tag"></form></span>
+ <span class="tag"></div></span>
+ );
+
+ }
+}
+
+AddVehicleForm.propTypes = {
+ <span class="key">makes</span>: array,
+ <span class="key">models</span>: array,
+ <span class="key">drivers</span>: array,
+ <span class="key">onSubmit</span>: func
+};
+
+<span class="reserved">export</span> <span class="keyword">default</span> AddVehicleForm;</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-react-app/4/guide/pages/rendering.html b/guides/building-a-react-app/4/guide/pages/rendering.html
index 474a7198d0b..60b54e3c604 100644
--- a/guides/building-a-react-app/4/guide/pages/rendering.html
+++ b/guides/building-a-react-app/4/guide/pages/rendering.html
@@ -96,7 +96,13 @@ function addJsClass() {
<div class="listingblock">
<div class="title">webpack.config.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>webpack.config.js[role=include]
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="keyword">var</span> path = require(<span class="string"><span class="delimiter">'</span><span class="content">path</span><span class="delimiter">'</span></span>);
+
+module.exports = {
+ <span class="key">entry</span>: {
+ <span class="key">index</span>: <span class="string"><span class="delimiter">'</span><span class="content">./src/main/webapp/index.js</span><span class="delimiter">'</span></span>, <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">garage</span>: <span class="string"><span class="delimiter">'</span><span class="content">./src/main/webapp/app/Garage.js</span><span class="delimiter">'</span></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ },
<span class="comment">//...</span></code></pre>
</div>
</div>
@@ -122,7 +128,11 @@ function addJsClass() {
<div class="title">webpack.config.js</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="javascript"><span class="comment">//...</span>
-link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>webpack.config.js[role=include]
+output: {
+ <span class="key">path</span>: path.join(__dirname, <span class="string"><span class="delimiter">'</span><span class="content">grails-app/assets/javascripts</span><span class="delimiter">'</span></span>),
+ <span class="key">publicPath</span>: <span class="string"><span class="delimiter">'</span><span class="content">/assets/</span><span class="delimiter">'</span></span>,
+ <span class="key">filename</span>: <span class="string"><span class="delimiter">'</span><span class="content">bundle-[name].js</span><span class="delimiter">'</span></span>
+},
<span class="comment">//...</span></code></pre>
</div>
</div>
@@ -182,7 +192,11 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">grails-app/controllers/demo/GarageController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/GarageController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="type">class</span> <span class="class">GarageController</span> {
+ <span class="keyword">def</span> <span class="function">index</span>() { }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -191,7 +205,24 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">grails-app/views/garage/index.gsp</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="xml">link:../snippets/grails-app/views/garage/index.gsp[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="xml"><span class="doctype"><!doctype html></span>
+<span class="tag"><html></span>
+<span class="tag"><head></span>
+ <span class="tag"><meta</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">layout</span><span class="delimiter">"</span></span> <span class="attribute-name">content</span>=<span class="string"><span class="delimiter">"</span><span class="content">main</span><span class="delimiter">"</span></span><span class="tag">/></span>
+ <span class="tag"><title></span>Garage<span class="tag"></title></span>
+ <span class="tag"><asset:link</span> <span class="attribute-name">rel</span>=<span class="string"><span class="delimiter">"</span><span class="content">icon</span><span class="delimiter">"</span></span> <span class="attribute-name">href</span>=<span class="string"><span class="delimiter">"</span><span class="content">favicon.ico</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">&q [...]
+<span class="tag"></head></span>
+<span class="tag"><body></span>
+
+<span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">content</span><span class="delimiter">"</span></span> <span class="attribute-name">role</span>=<span class="string"><span class="delimiter">"</span><span class="content">main</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><section</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row colset-2-its</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">garage</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"><asset:javascript</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">bundle-garage.js</span><span class="delimiter">"</span></span> <span class="tag">/></span>
+ <span class="tag"></section></span>
+<span class="tag"></div></span>
+
+<span class="tag"></body></span>
+<span class="tag"></html></span></code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/building-a-react-app/4/guide/pages/vehicles.html b/guides/building-a-react-app/4/guide/pages/vehicles.html
index 9ed47aeadb7..5174324d9f6 100644
--- a/guides/building-a-react-app/4/guide/pages/vehicles.html
+++ b/guides/building-a-react-app/4/guide/pages/vehicles.html
@@ -98,7 +98,51 @@ function addJsClass() {
</div>
<div class="listingblock">
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/main/webapp/app/Vehicles.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> React from <span class="string"><span class="delimiter">'</span><span class="content">react</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {Table} from <span class="string"><span class="delimiter">'</span><span class="content">react-bootstrap</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {array} from <span class="string"><span class="delimiter">'</span><span class="content">prop-types</span><span class="delimiter">'</span></span>;
+
+<span class="reserved">class</span> Vehicles <span class="reserved">extends</span> React.Component {
+
+ render() {
+ <span class="keyword">function</span> <span class="function">renderVehicleRow</span>(vehicle) {
+
+ <span class="keyword">return</span> (<tr key={vehicle.id}>
+ <span class="tag"><td></span>{vehicle.id}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.make.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.model.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.driver.name}<span class="tag"></td></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">tr>);
+ }
+
+
+ return <div>
+ <Table striped bordered condensed hover>
+ <thead>
+ <tr>
+ <th>ID<</span><span class="delimiter">/</span></span>th>
+ <span class="tag"><th></span>Name<span class="tag"></th></span>
+ <span class="tag"><th></span>Make<span class="tag"></th></span>
+ <span class="tag"><th></span>Model<span class="tag"></th></span>
+ <span class="tag"><th></span>Driver<span class="tag"></th></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">tr>
+ <</span><span class="delimiter">/</span></span>thead>
+ <span class="tag"><tbody></span>
+
+ {this.props.vehicles.map(renderVehicleRow)}
+
+ <span class="tag"></tbody></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">Table>
+ <</span><span class="delimiter">/</span></span>div>;
+ }
+}
+
+Vehicles.propTypes = {
+ <span class="key">vehicles</span>: array
+};
+
+<span class="reserved">export</span> <span class="keyword">default</span> Vehicles;</code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/building-a-react-app/4/guide/runningTheApp.html b/guides/building-a-react-app/4/guide/runningTheApp.html
index e40560d3c40..fa13f223a38 100644
--- a/guides/building-a-react-app/4/guide/runningTheApp.html
+++ b/guides/building-a-react-app/4/guide/runningTheApp.html
@@ -230,7 +230,25 @@ Connection: close
<div class="listingblock">
<div class="title">grails-app/views/vehicle/_vehicle.gson</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/views/vehicle/_vehicle.gson[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+model {
+ Vehicle vehicle
+}
+json {
+ id vehicle.id
+
+ name vehicle.name
+
+ make <span class="key">name</span>: vehicle.make.name,
+ <span class="key">id</span>: vehicle.make.id
+
+ model <span class="key">name</span>: vehicle.model.name,
+ <span class="key">id</span>: vehicle.model.id
+
+ driver <span class="key">name</span>: vehicle.driver.name,
+ <span class="key">id</span>: vehicle.driver.id
+}</code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/building-a-react-app/4/guide/single.html b/guides/building-a-react-app/4/guide/single.html
index e832aeafdb9..cfe0e5df683 100644
--- a/guides/building-a-react-app/4/guide/single.html
+++ b/guides/building-a-react-app/4/guide/single.html
@@ -305,25 +305,77 @@ $ grails create-domain-class demo.Model</code></pre>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Vehicle.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Vehicle.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/vehicle</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Vehicle</span> {
+
+ <span class="predefined-type">String</span> name
+
+ Make make
+ Model model
+
+ <span class="directive">static</span> belongsTo = [<span class="key">driver</span>: <span class="predefined-type">Driver</span>]
+
+ <span class="directive">static</span> constraints = {
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Driver.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/<span class="predefined-type">Driver</span>.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/driver</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Driver</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> hasMany = [ <span class="key">vehicles</span>: Vehicle ]
+
+ <span class="directive">static</span> constraints = {
+ vehicles <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Make.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Make.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/make</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Make</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> constraints = {
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Model.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Model.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/model</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Model</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> constraints = {
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -332,7 +384,35 @@ $ grails create-domain-class demo.Model</code></pre>
<div class="listingblock">
<div class="title">grails-app/init/demo/BootStrap.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/init/demo/BootStrap.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">demo.Driver</span>
+<span class="keyword">import</span> <span class="include">demo.Make</span>
+<span class="keyword">import</span> <span class="include">demo.Model</span>
+<span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+<span class="type">class</span> <span class="class">BootStrap</span> {
+
+ <span class="keyword">def</span> init = { servletContext ->
+ log.info <span class="string"><span class="delimiter">"</span><span class="content">Loading database...</span><span class="delimiter">"</span></span>
+ <span class="keyword">def</span> driver1 = <span class="keyword">new</span> <span class="predefined-type">Driver</span>(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Susan</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> driver2 = <span class="keyword">new</span> <span class="predefined-type">Driver</span>(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Pedro</span><span class="delimiter">"</span></span>).save()
+
+ <span class="keyword">def</span> nissan = <span class="keyword">new</span> Make(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Nissan</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> ford = <span class="keyword">new</span> Make(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Ford</span><span class="delimiter">"</span></span>).save()
+
+ <span class="keyword">def</span> titan = <span class="keyword">new</span> Model(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Titan</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> leaf = <span class="keyword">new</span> Model(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Leaf</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> windstar = <span class="keyword">new</span> Model(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Windstar</span><span class="delimiter">"</span></span>).save()
+
+ <span class="keyword">new</span> Vehicle(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Pickup</span><span class="delimiter">"</span></span>, <span class="key">driver</span>: driver1, <span class="key">make</span>: nissan, <span class="key">model</span>: titan).save()
+ <span class="keyword">new</span> Vehicle(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Economy</span><span class="delimiter">"</span></span>, <span class="key">driver</span>: driver1, <span class="key">make</span>: nissan, <span class="key">model</span>: leaf).save()
+ <span class="keyword">new</span> Vehicle(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Minivan</span><span class="delimiter">"</span></span>, <span class="key">driver</span>: driver2, <span class="key">make</span>: ford, <span class="key">model</span>: windstar).save()
+
+ }
+ <span class="keyword">def</span> destroy = {
+ }
+}</code></pre>
</div>
</div>
@@ -481,7 +561,25 @@ Connection: close
<div class="listingblock">
<div class="title">grails-app/views/vehicle/_vehicle.gson</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/views/vehicle/_vehicle.gson[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+model {
+ Vehicle vehicle
+}
+json {
+ id vehicle.id
+
+ name vehicle.name
+
+ make <span class="key">name</span>: vehicle.make.name,
+ <span class="key">id</span>: vehicle.make.id
+
+ model <span class="key">name</span>: vehicle.model.name,
+ <span class="key">id</span>: vehicle.model.id
+
+ driver <span class="key">name</span>: vehicle.driver.name,
+ <span class="key">id</span>: vehicle.driver.id
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -535,7 +633,51 @@ Connection: close
</div>
<div class="listingblock">
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/main/webapp/app/Vehicles.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> React from <span class="string"><span class="delimiter">'</span><span class="content">react</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {Table} from <span class="string"><span class="delimiter">'</span><span class="content">react-bootstrap</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {array} from <span class="string"><span class="delimiter">'</span><span class="content">prop-types</span><span class="delimiter">'</span></span>;
+
+<span class="reserved">class</span> Vehicles <span class="reserved">extends</span> React.Component {
+
+ render() {
+ <span class="keyword">function</span> <span class="function">renderVehicleRow</span>(vehicle) {
+
+ <span class="keyword">return</span> (<tr key={vehicle.id}>
+ <span class="tag"><td></span>{vehicle.id}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.make.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.model.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.driver.name}<span class="tag"></td></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">tr>);
+ }
+
+
+ return <div>
+ <Table striped bordered condensed hover>
+ <thead>
+ <tr>
+ <th>ID<</span><span class="delimiter">/</span></span>th>
+ <span class="tag"><th></span>Name<span class="tag"></th></span>
+ <span class="tag"><th></span>Make<span class="tag"></th></span>
+ <span class="tag"><th></span>Model<span class="tag"></th></span>
+ <span class="tag"><th></span>Driver<span class="tag"></th></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">tr>
+ <</span><span class="delimiter">/</span></span>thead>
+ <span class="tag"><tbody></span>
+
+ {this.props.vehicles.map(renderVehicleRow)}
+
+ <span class="tag"></tbody></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">Table>
+ <</span><span class="delimiter">/</span></span>div>;
+ }
+}
+
+Vehicles.propTypes = {
+ <span class="key">vehicles</span>: array
+};
+
+<span class="reserved">export</span> <span class="keyword">default</span> Vehicles;</code></pre>
</div>
</div>
<div class="paragraph">
@@ -624,7 +766,13 @@ ReactDOM.render(<Garage />, document.getElementById(<span class="string"><
<div class="listingblock">
<div class="title">webpack.config.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>webpack.config.js[role=include]
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="keyword">var</span> path = require(<span class="string"><span class="delimiter">'</span><span class="content">path</span><span class="delimiter">'</span></span>);
+
+module.exports = {
+ <span class="key">entry</span>: {
+ <span class="key">index</span>: <span class="string"><span class="delimiter">'</span><span class="content">./src/main/webapp/index.js</span><span class="delimiter">'</span></span>, <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">garage</span>: <span class="string"><span class="delimiter">'</span><span class="content">./src/main/webapp/app/Garage.js</span><span class="delimiter">'</span></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ },
<span class="comment">//...</span></code></pre>
</div>
</div>
@@ -650,7 +798,11 @@ ReactDOM.render(<Garage />, document.getElementById(<span class="string"><
<div class="title">webpack.config.js</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="javascript"><span class="comment">//...</span>
-link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>webpack.config.js[role=include]
+output: {
+ <span class="key">path</span>: path.join(__dirname, <span class="string"><span class="delimiter">'</span><span class="content">grails-app/assets/javascripts</span><span class="delimiter">'</span></span>),
+ <span class="key">publicPath</span>: <span class="string"><span class="delimiter">'</span><span class="content">/assets/</span><span class="delimiter">'</span></span>,
+ <span class="key">filename</span>: <span class="string"><span class="delimiter">'</span><span class="content">bundle-[name].js</span><span class="delimiter">'</span></span>
+},
<span class="comment">//...</span></code></pre>
</div>
</div>
@@ -710,7 +862,11 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">grails-app/controllers/demo/GarageController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/GarageController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="type">class</span> <span class="class">GarageController</span> {
+ <span class="keyword">def</span> <span class="function">index</span>() { }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -719,7 +875,24 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">grails-app/views/garage/index.gsp</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="xml">link:../snippets/grails-app/views/garage/index.gsp[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="xml"><span class="doctype"><!doctype html></span>
+<span class="tag"><html></span>
+<span class="tag"><head></span>
+ <span class="tag"><meta</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">layout</span><span class="delimiter">"</span></span> <span class="attribute-name">content</span>=<span class="string"><span class="delimiter">"</span><span class="content">main</span><span class="delimiter">"</span></span><span class="tag">/></span>
+ <span class="tag"><title></span>Garage<span class="tag"></title></span>
+ <span class="tag"><asset:link</span> <span class="attribute-name">rel</span>=<span class="string"><span class="delimiter">"</span><span class="content">icon</span><span class="delimiter">"</span></span> <span class="attribute-name">href</span>=<span class="string"><span class="delimiter">"</span><span class="content">favicon.ico</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">&q [...]
+<span class="tag"></head></span>
+<span class="tag"><body></span>
+
+<span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">content</span><span class="delimiter">"</span></span> <span class="attribute-name">role</span>=<span class="string"><span class="delimiter">"</span><span class="content">main</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><section</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row colset-2-its</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">garage</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"><asset:javascript</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">bundle-garage.js</span><span class="delimiter">"</span></span> <span class="tag">/></span>
+ <span class="tag"></section></span>
+<span class="tag"></div></span>
+
+<span class="tag"></body></span>
+<span class="tag"></html></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -809,7 +982,100 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">src/main/webapp/app/AddVehicleForm.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/main/webapp/app/AddVehicleForm.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> React from <span class="string"><span class="delimiter">'</span><span class="content">react</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {array, func} from <span class="string"><span class="delimiter">'</span><span class="content">prop-types</span><span class="delimiter">'</span></span>;
+
+<span class="reserved">class</span> AddVehicleForm <span class="reserved">extends</span> React.Component {
+
+ constructor(props) {
+ <span class="reserved">super</span>(props);
+ <span class="local-variable">this</span>.state = { <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>,
+ <span class="key">make</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>},
+ <span class="key">model</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>},
+ <span class="key">driver</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>}};
+ }
+
+ handleSubmit = (event) => { <i class="conum" data-value="2"></i><b>(2)</b>
+ event.preventDefault();
+
+ const {name, make, model, driver} = <span class="local-variable">this</span>.state;
+
+ <span class="keyword">if</span> (!name || !make.id || !model.id || !driver.id) {
+ console.warn(<span class="string"><span class="delimiter">"</span><span class="content">missing required field!</span><span class="delimiter">"</span></span>);
+ <span class="keyword">return</span>;
+ }
+ <span class="local-variable">this</span>.props.onSubmit( {name, make, model, driver} ); <i class="conum" data-value="3"></i><b>(3)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>, <span class="key">make</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>}, <span class="key">model</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>} [...]
+ };
+
+ handleNameChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">name</span>: event.target.value });
+ };
+
+ handleMakeChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">make</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+ handleModelChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">model</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+ handleDriverChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">driver</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+
+ render() {
+
+ <span class="keyword">function</span> <span class="function">renderSelectList</span>(item) { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="keyword">return</span> <span class="tag"><option</span> <span class="attribute-name">key</span>=<span class="error">{</span><span class="attribute-value">item.id</span><span class="error">}</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">item.id</span><span class="error">}</span><span class="tag">></span>{item.name}<span class="tag"></option></span>
+ }
+
+ <span class="keyword">return</span>(
+ <span class="tag"><div></span>
+ <span class="tag"><h3></span>Add a Vehicle:<span class="tag"></h3></span>
+ <span class="tag"><form</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form form-inline</span><span class="delimiter">"</span></span> <span class="attribute-name">onSubmit</span>=<span class="error">{</span><span class="attribute-value">this.handleSubmit</span><span class="error">}</span> <span class="tag">></span>
+ <span class="tag"><label></span>Name<span class="tag"></label></span>
+ <span class="tag"><input</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delim [...]
+
+ <span class="tag"><label></span>Make<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">make</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span cla [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleMakeChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Make...<span class="tag"></option></span>
+ {this.props.makes.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><label></span>Model<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">model</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span cl [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleModelChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Model...<span class="tag"></option></span>
+ {this.props.models.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><label></span>Driver<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">driver</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span c [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleDriverChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Driver...<span class="tag"></option></span>
+ {this.props.drivers.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><input</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn btn-success</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">submit</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="string"><span class [...]
+ <span class="tag"></form></span>
+ <span class="tag"></div></span>
+ );
+
+ }
+}
+
+AddVehicleForm.propTypes = {
+ <span class="key">makes</span>: array,
+ <span class="key">models</span>: array,
+ <span class="key">drivers</span>: array,
+ <span class="key">onSubmit</span>: func
+};
+
+<span class="reserved">export</span> <span class="keyword">default</span> AddVehicleForm;</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-react-app/4/guide/writingReactComponents.html b/guides/building-a-react-app/4/guide/writingReactComponents.html
index 15c00ebd984..1295234a34e 100644
--- a/guides/building-a-react-app/4/guide/writingReactComponents.html
+++ b/guides/building-a-react-app/4/guide/writingReactComponents.html
@@ -132,7 +132,51 @@ function addJsClass() {
</div>
<div class="listingblock">
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/main/webapp/app/Vehicles.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> React from <span class="string"><span class="delimiter">'</span><span class="content">react</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {Table} from <span class="string"><span class="delimiter">'</span><span class="content">react-bootstrap</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {array} from <span class="string"><span class="delimiter">'</span><span class="content">prop-types</span><span class="delimiter">'</span></span>;
+
+<span class="reserved">class</span> Vehicles <span class="reserved">extends</span> React.Component {
+
+ render() {
+ <span class="keyword">function</span> <span class="function">renderVehicleRow</span>(vehicle) {
+
+ <span class="keyword">return</span> (<tr key={vehicle.id}>
+ <span class="tag"><td></span>{vehicle.id}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.make.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.model.name}<span class="tag"></td></span>
+ <span class="tag"><td></span>{vehicle.driver.name}<span class="tag"></td></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">tr>);
+ }
+
+
+ return <div>
+ <Table striped bordered condensed hover>
+ <thead>
+ <tr>
+ <th>ID<</span><span class="delimiter">/</span></span>th>
+ <span class="tag"><th></span>Name<span class="tag"></th></span>
+ <span class="tag"><th></span>Make<span class="tag"></th></span>
+ <span class="tag"><th></span>Model<span class="tag"></th></span>
+ <span class="tag"><th></span>Driver<span class="tag"></th></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">tr>
+ <</span><span class="delimiter">/</span></span>thead>
+ <span class="tag"><tbody></span>
+
+ {this.props.vehicles.map(renderVehicleRow)}
+
+ <span class="tag"></tbody></span>
+ <<span class="regexp"><span class="delimiter">/</span><span class="content">Table>
+ <</span><span class="delimiter">/</span></span>div>;
+ }
+}
+
+Vehicles.propTypes = {
+ <span class="key">vehicles</span>: array
+};
+
+<span class="reserved">export</span> <span class="keyword">default</span> Vehicles;</code></pre>
</div>
</div>
<div class="paragraph">
@@ -221,7 +265,13 @@ ReactDOM.render(<Garage />, document.getElementById(<span class="string"><
<div class="listingblock">
<div class="title">webpack.config.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>webpack.config.js[role=include]
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="keyword">var</span> path = require(<span class="string"><span class="delimiter">'</span><span class="content">path</span><span class="delimiter">'</span></span>);
+
+module.exports = {
+ <span class="key">entry</span>: {
+ <span class="key">index</span>: <span class="string"><span class="delimiter">'</span><span class="content">./src/main/webapp/index.js</span><span class="delimiter">'</span></span>, <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">garage</span>: <span class="string"><span class="delimiter">'</span><span class="content">./src/main/webapp/app/Garage.js</span><span class="delimiter">'</span></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ },
<span class="comment">//...</span></code></pre>
</div>
</div>
@@ -247,7 +297,11 @@ ReactDOM.render(<Garage />, document.getElementById(<span class="string"><
<div class="title">webpack.config.js</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="javascript"><span class="comment">//...</span>
-link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>webpack.config.js[role=include]
+output: {
+ <span class="key">path</span>: path.join(__dirname, <span class="string"><span class="delimiter">'</span><span class="content">grails-app/assets/javascripts</span><span class="delimiter">'</span></span>),
+ <span class="key">publicPath</span>: <span class="string"><span class="delimiter">'</span><span class="content">/assets/</span><span class="delimiter">'</span></span>,
+ <span class="key">filename</span>: <span class="string"><span class="delimiter">'</span><span class="content">bundle-[name].js</span><span class="delimiter">'</span></span>
+},
<span class="comment">//...</span></code></pre>
</div>
</div>
@@ -307,7 +361,11 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">grails-app/controllers/demo/GarageController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/GarageController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="type">class</span> <span class="class">GarageController</span> {
+ <span class="keyword">def</span> <span class="function">index</span>() { }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -316,7 +374,24 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">grails-app/views/garage/index.gsp</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="xml">link:../snippets/grails-app/views/garage/index.gsp[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="xml"><span class="doctype"><!doctype html></span>
+<span class="tag"><html></span>
+<span class="tag"><head></span>
+ <span class="tag"><meta</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">layout</span><span class="delimiter">"</span></span> <span class="attribute-name">content</span>=<span class="string"><span class="delimiter">"</span><span class="content">main</span><span class="delimiter">"</span></span><span class="tag">/></span>
+ <span class="tag"><title></span>Garage<span class="tag"></title></span>
+ <span class="tag"><asset:link</span> <span class="attribute-name">rel</span>=<span class="string"><span class="delimiter">"</span><span class="content">icon</span><span class="delimiter">"</span></span> <span class="attribute-name">href</span>=<span class="string"><span class="delimiter">"</span><span class="content">favicon.ico</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">&q [...]
+<span class="tag"></head></span>
+<span class="tag"><body></span>
+
+<span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">content</span><span class="delimiter">"</span></span> <span class="attribute-name">role</span>=<span class="string"><span class="delimiter">"</span><span class="content">main</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><section</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row colset-2-its</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">garage</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"><asset:javascript</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">bundle-garage.js</span><span class="delimiter">"</span></span> <span class="tag">/></span>
+ <span class="tag"></section></span>
+<span class="tag"></div></span>
+
+<span class="tag"></body></span>
+<span class="tag"></html></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -406,7 +481,100 @@ link:..<span class="regexp"><span class="delimiter">/</span><span class="content
<div class="listingblock">
<div class="title">src/main/webapp/app/AddVehicleForm.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/main/webapp/app/AddVehicleForm.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> React from <span class="string"><span class="delimiter">'</span><span class="content">react</span><span class="delimiter">'</span></span>;
+<span class="reserved">import</span> {array, func} from <span class="string"><span class="delimiter">'</span><span class="content">prop-types</span><span class="delimiter">'</span></span>;
+
+<span class="reserved">class</span> AddVehicleForm <span class="reserved">extends</span> React.Component {
+
+ constructor(props) {
+ <span class="reserved">super</span>(props);
+ <span class="local-variable">this</span>.state = { <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>,
+ <span class="key">make</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>},
+ <span class="key">model</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>},
+ <span class="key">driver</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>}};
+ }
+
+ handleSubmit = (event) => { <i class="conum" data-value="2"></i><b>(2)</b>
+ event.preventDefault();
+
+ const {name, make, model, driver} = <span class="local-variable">this</span>.state;
+
+ <span class="keyword">if</span> (!name || !make.id || !model.id || !driver.id) {
+ console.warn(<span class="string"><span class="delimiter">"</span><span class="content">missing required field!</span><span class="delimiter">"</span></span>);
+ <span class="keyword">return</span>;
+ }
+ <span class="local-variable">this</span>.props.onSubmit( {name, make, model, driver} ); <i class="conum" data-value="3"></i><b>(3)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>, <span class="key">make</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>}, <span class="key">model</span>: {<span class="key">id</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>} [...]
+ };
+
+ handleNameChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">name</span>: event.target.value });
+ };
+
+ handleMakeChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">make</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+ handleModelChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">model</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+ handleDriverChange = (event) => { <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="local-variable">this</span>.setState({ <span class="key">driver</span>: {<span class="key">id</span>: event.target.value} });
+ };
+
+
+ render() {
+
+ <span class="keyword">function</span> <span class="function">renderSelectList</span>(item) { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="keyword">return</span> <span class="tag"><option</span> <span class="attribute-name">key</span>=<span class="error">{</span><span class="attribute-value">item.id</span><span class="error">}</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">item.id</span><span class="error">}</span><span class="tag">></span>{item.name}<span class="tag"></option></span>
+ }
+
+ <span class="keyword">return</span>(
+ <span class="tag"><div></span>
+ <span class="tag"><h3></span>Add a Vehicle:<span class="tag"></h3></span>
+ <span class="tag"><form</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form form-inline</span><span class="delimiter">"</span></span> <span class="attribute-name">onSubmit</span>=<span class="error">{</span><span class="attribute-value">this.handleSubmit</span><span class="error">}</span> <span class="tag">></span>
+ <span class="tag"><label></span>Name<span class="tag"></label></span>
+ <span class="tag"><input</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delim [...]
+
+ <span class="tag"><label></span>Make<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">make</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span cla [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleMakeChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Make...<span class="tag"></option></span>
+ {this.props.makes.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><label></span>Model<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">model</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span cl [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleModelChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Model...<span class="tag"></option></span>
+ {this.props.models.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><label></span>Driver<span class="tag"></label></span>
+ <span class="tag"><select</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">driver</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="error">{</span><span c [...]
+ <span class="attribute-name">onChange</span>=<span class="error">{</span><span class="attribute-value">this.handleDriverChange</span><span class="error">}</span><span class="tag">></span> {/*<span class="tag"><6></span>*/}
+ <span class="tag"><option</span> <span class="attribute-name">value</span>=<span class="error">{</span><span class="attribute-value">null</span><span class="error">}</span><span class="tag">></span>Select a Driver...<span class="tag"></option></span>
+ {this.props.drivers.map(renderSelectList)} {/*<span class="tag"><5></span>*/}
+ <span class="tag"></select></span>
+
+ <span class="tag"><input</span> <span class="attribute-name">className</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn btn-success</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">submit</span><span class="delimiter">"</span></span> <span class="attribute-name">value</span>=<span class="string"><span class [...]
+ <span class="tag"></form></span>
+ <span class="tag"></div></span>
+ );
+
+ }
+}
+
+AddVehicleForm.propTypes = {
+ <span class="key">makes</span>: array,
+ <span class="key">models</span>: array,
+ <span class="key">drivers</span>: array,
+ <span class="key">onSubmit</span>: func
+};
+
+<span class="reserved">export</span> <span class="keyword">default</span> AddVehicleForm;</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-react-app/4/guide/writingTheApp.html b/guides/building-a-react-app/4/guide/writingTheApp.html
index 7f9f5e34830..11755d6ebfc 100644
--- a/guides/building-a-react-app/4/guide/writingTheApp.html
+++ b/guides/building-a-react-app/4/guide/writingTheApp.html
@@ -107,25 +107,77 @@ $ grails create-domain-class demo.Model</code></pre>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Vehicle.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Vehicle.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/vehicle</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Vehicle</span> {
+
+ <span class="predefined-type">String</span> name
+
+ Make make
+ Model model
+
+ <span class="directive">static</span> belongsTo = [<span class="key">driver</span>: <span class="predefined-type">Driver</span>]
+
+ <span class="directive">static</span> constraints = {
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Driver.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/<span class="predefined-type">Driver</span>.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/driver</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Driver</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> hasMany = [ <span class="key">vehicles</span>: Vehicle ]
+
+ <span class="directive">static</span> constraints = {
+ vehicles <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Make.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Make.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/make</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Make</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> constraints = {
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">grails-app/grails-app/domain/demo/Model.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Model.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/model</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Model</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> constraints = {
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -134,7 +186,35 @@ $ grails create-domain-class demo.Model</code></pre>
<div class="listingblock">
<div class="title">grails-app/init/demo/BootStrap.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/init/demo/BootStrap.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">demo.Driver</span>
+<span class="keyword">import</span> <span class="include">demo.Make</span>
+<span class="keyword">import</span> <span class="include">demo.Model</span>
+<span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+<span class="type">class</span> <span class="class">BootStrap</span> {
+
+ <span class="keyword">def</span> init = { servletContext ->
+ log.info <span class="string"><span class="delimiter">"</span><span class="content">Loading database...</span><span class="delimiter">"</span></span>
+ <span class="keyword">def</span> driver1 = <span class="keyword">new</span> <span class="predefined-type">Driver</span>(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Susan</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> driver2 = <span class="keyword">new</span> <span class="predefined-type">Driver</span>(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Pedro</span><span class="delimiter">"</span></span>).save()
+
+ <span class="keyword">def</span> nissan = <span class="keyword">new</span> Make(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Nissan</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> ford = <span class="keyword">new</span> Make(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Ford</span><span class="delimiter">"</span></span>).save()
+
+ <span class="keyword">def</span> titan = <span class="keyword">new</span> Model(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Titan</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> leaf = <span class="keyword">new</span> Model(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Leaf</span><span class="delimiter">"</span></span>).save()
+ <span class="keyword">def</span> windstar = <span class="keyword">new</span> Model(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Windstar</span><span class="delimiter">"</span></span>).save()
+
+ <span class="keyword">new</span> Vehicle(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Pickup</span><span class="delimiter">"</span></span>, <span class="key">driver</span>: driver1, <span class="key">make</span>: nissan, <span class="key">model</span>: titan).save()
+ <span class="keyword">new</span> Vehicle(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Economy</span><span class="delimiter">"</span></span>, <span class="key">driver</span>: driver1, <span class="key">make</span>: nissan, <span class="key">model</span>: leaf).save()
+ <span class="keyword">new</span> Vehicle(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Minivan</span><span class="delimiter">"</span></span>, <span class="key">driver</span>: driver2, <span class="key">make</span>: ford, <span class="key">model</span>: windstar).save()
+
+ }
+ <span class="keyword">def</span> destroy = {
+ }
+}</code></pre>
</div>
</div>
diff --git a/guides/building-a-vue-app/4/guide/index.html b/guides/building-a-vue-app/4/guide/index.html
index 0efe9eba6a9..f872da71aa0 100644
--- a/guides/building-a-vue-app/4/guide/index.html
+++ b/guides/building-a-vue-app/4/guide/index.html
@@ -352,25 +352,66 @@ $ grails create-domain-class demo.Model</code></pre>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Vehicle.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/Vehicle.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/vehicle</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Vehicle</span> {
+
+ <span class="predefined-type">String</span> name
+
+ Make make
+ Model model
+
+ <span class="directive">static</span> belongsTo = [<span class="key">driver</span>: <span class="predefined-type">Driver</span>]
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Driver.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/<span class="predefined-type">Driver</span>.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/driver</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Driver</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> hasMany = [ <span class="key">vehicles</span>: Vehicle ]
+
+ <span class="directive">static</span> constraints = {
+ vehicles <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Make.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/Make.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/make</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Make</span> {
+ <span class="predefined-type">String</span> name
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Model.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/Model.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/model</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Model</span> {
+ <span class="predefined-type">String</span> name
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -381,31 +422,89 @@ Let’s preload some data with the help of
<div class="listingblock">
<div class="title">server/grails-app/services/demo/MakeDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/MakeDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(Make)
+<span class="type">interface</span> MakeDataService {
+ Make save(<span class="predefined-type">String</span> name)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/services/demo/ModelDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/ModelDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(Model)
+<span class="type">interface</span> ModelDataService {
+ Model save(<span class="predefined-type">String</span> name)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/services/demo/DriverDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/DriverDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(<span class="predefined-type">Driver</span>)
+<span class="type">interface</span> DriverDataService {
+ <span class="predefined-type">Driver</span> save(<span class="predefined-type">String</span> name)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/services/demo/VehicleDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/VehicleDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(Vehicle)
+<span class="type">interface</span> VehicleDataService {
+ Vehicle save(<span class="predefined-type">String</span> name, <span class="predefined-type">Driver</span> driver, Make make, Model model)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/init/demo/BootStrap.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/init/demo/BootStrap.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">groovy.transform.CompileStatic</span>
+
+<span class="annotation">@CompileStatic</span>
+<span class="type">class</span> <span class="class">BootStrap</span> {
+
+ DriverDataService driverDataService
+ MakeDataService makeDataService
+ ModelDataService modelDataService
+ VehicleDataService vehicleDataService
+ <span class="keyword">def</span> init = { servletContext ->
+ log.info <span class="string"><span class="delimiter">"</span><span class="content">Loading database...</span><span class="delimiter">"</span></span>
+ <span class="predefined-type">Driver</span> driver1 = driverDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Susan</span><span class="delimiter">"</span></span>)
+ <span class="predefined-type">Driver</span> driver2 = driverDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Pedro</span><span class="delimiter">"</span></span>)
+
+ Make nissan = makeDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Nissan</span><span class="delimiter">"</span></span>)
+ Make ford = makeDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Ford</span><span class="delimiter">"</span></span>)
+
+ Model titan = modelDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Titan</span><span class="delimiter">"</span></span>)
+ Model leaf = modelDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Leaf</span><span class="delimiter">"</span></span>)
+ Model windstar = modelDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Windstar</span><span class="delimiter">"</span></span>)
+
+ vehicleDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Pickup</span><span class="delimiter">"</span></span>, driver1, nissan, titan)
+ vehicleDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Economy</span><span class="delimiter">"</span></span>, driver1, nissan, leaf)
+ vehicleDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Minivan</span><span class="delimiter">"</span></span>, driver2, ford, windstar)
+
+ }
+ <span class="keyword">def</span> destroy = {
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -563,7 +662,25 @@ Connection: close
<div class="listingblock">
<div class="title">server/grails-app/views/vehicle/_vehicle.gson</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/views/vehicle/_vehicle.gson[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+model {
+ Vehicle vehicle
+}
+json {
+ id vehicle.id
+
+ name vehicle.name
+
+ make <span class="key">name</span>: vehicle.make.name,
+ <span class="key">id</span>: vehicle.make.id
+
+ model <span class="key">name</span>: vehicle.model.name,
+ <span class="key">id</span>: vehicle.model.id
+
+ driver <span class="key">name</span>: vehicle.driver.name,
+ <span class="key">id</span>: vehicle.driver.id
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -700,7 +817,23 @@ While they serve a similar function, please remember that the Vue/webpack enviro
<div class="listingblock">
<div class="title">/client/src/components/AppHeader.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/AppHeader.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">app-header-template</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">container</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">jumbotron</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><img</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">../assets/logo.png</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><h1></span>Welcome to the Garage<span class="tag"></h1></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">app-header</span><span class="delimiter">'</span></span>
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -730,7 +863,24 @@ Every <code><template></code> must contain only one root-level element.
<div class="listingblock">
<div class="title">/client/src/components/VehicleAddHeader.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/form/VehicleFormHeader.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">add-vehicle-header-template</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">headerRow</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><h3></span>Add a Vehicle:<span class="tag"></h3></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">vehicle-form-header</span><span class="delimiter">'</span></span>
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="admonitionblock tip">
@@ -772,7 +922,38 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/form/FieldSelect.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/form/FieldSelect.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">driverSelect-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span c [...]
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-group</span><span class="delimiter">"</span></span><span class="tag">></span> <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="tag"><select</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">selected</span><span class="delimiter">"</span></span> <span class="error">@</span><span class="attribute-name">change</span>=<span class [...]
+ <span class="tag"><option</span> <span class="attribute-name">disabled</span> <span class="attribute-name">:value</span>=<span class="string"><span class="delimiter">"</span><span class="content">null</span><span class="delimiter">"</span></span><span class="tag">></span>Select a {{field}}<span class="tag"></option></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="tag"><option</span> <span class="attribute-name">v-for</span>=<span class="string"><span class="delimiter">"</span><span class="content">value in values</span><span class="delimiter">"</span></span> <span class="attribute-name">:value</span>=<span class="string"><span class="delimiter">"</span><span class="content">value.id</span><span class="delimiter">"</span></span> <span class="attribute-name">:key</span>=<span class="string"><span class="del [...]
+ {{ value.name }}
+ <span class="tag"></option></span>
+ <span class="tag"></select></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">values</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">field</span><span class="delimiter">'</span></span>], <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="function">data</span>: <span class="keyword">function</span> () { <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="keyword">return</span> {
+ <span class="key">selected</span>: <span class="predefined-constant">null</span> <i class="conum" data-value="3"></i><b>(3)</b>
+ }
+ },
+ <span class="key">methods</span>: { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="function">updateValue</span>: <span class="keyword">function</span> () { <i class="conum" data-value="6"></i><b>(6)</b>
+ <span class="local-variable">this</span>.<span class="predefined">$emit</span>(<span class="string"><span class="delimiter">'</span><span class="content">input</span><span class="delimiter">'</span></span>, <span class="local-variable">this</span>.selected)
+ }
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -840,7 +1021,26 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/TableRow.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/table/TableRow.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">tablerow-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span [...]
+ <span class="tag"><tr></span> <span class="comment"><!-- 1 --></span>
+ <span class="tag"><td></span>{{ item.id }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.make.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.model.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.driver.name }}<span class="tag"></td></span>
+ <span class="tag"></tr></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">item</span><span class="delimiter">'</span></span>] <i class="conum" data-value="1"></i><b>(1)</b>
+}</span>
+<span class="tag"></script></span>
+
+<!-- Per Component Custom CSS Rules -->
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -857,7 +1057,36 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/table/VehicleTable.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/table/VehicleTable.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">fulltable-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><spa [...]
+ <span class="tag"><table</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">table</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><thead</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">thead-inverse</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><tr></span>
+ <span class="tag"><th></span>ID<span class="tag"></th></span>
+ <span class="tag"><th></span>Name<span class="tag"></th></span>
+ <span class="tag"><th></span>Make<span class="tag"></th></span>
+ <span class="tag"><th></span>Model<span class="tag"></th></span>
+ <span class="tag"><th></span>Driver<span class="tag"></th></span>
+ <span class="tag"></tr></span>
+ <span class="tag"></thead></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="tag"><table-row</span> <span class="attribute-name">v-for</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle in vehicles</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:item</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle</span><span class="delimiter">"</span></span> <span class="attribute-name">:key</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.id</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></table-row></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="tag"></table></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> TableRow from <span class="string"><span class="delimiter">'</span><span class="content">./TableRow.vue</span><span class="delimiter">'</span></span> <i class="conum" data-value="3"></i><b>(3)</b>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">vehicles</span><span class="delimiter">'</span></span>],
+ <span class="key">components</span>: { <i class="conum" data-value="3"></i><b>(3)</b>
+ TableRow
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -931,7 +1160,64 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/form/VehicleForm.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/form/VehicleForm.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">add-vehicle-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span cl [...]
+ <span class="tag"><div></span>
+ <span class="tag"><vehicle-form-header</span><span class="tag">/></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">inputRow</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-3</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">input-group</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><input</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">placeholder</span>=<span class="string"><span class="de [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-7</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.make</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Make'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"><spa [...]
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.model</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Model'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"><s [...]
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.driver</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Driver'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"> [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-2</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn-group</span><span class="delimiter">"</span></span> <span class="attribute-name">role</span>=<span class="string"><span class="delimiter">"</span><span class="content">group</span><span class="delimiter">"</span></span> <span class="attribute-name">aria-label</span>=<span class="string"><span class="delimiter [...]
+ <span class="tag"><button</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">button</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn btn-success</span><span class="delimiter">"</span></span> <span class="error">@</span><span class="attribute-name">click</span>=<span cl [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> VehicleFormHeader from <span class="string"><span class="delimiter">'</span><span class="content">./VehicleFormHeader</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> FieldSelect from <span class="string"><span class="delimiter">'</span><span class="content">./FieldSelect</span><span class="delimiter">'</span></span>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">vehicle</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">makes</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">models</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="cont [...]
+ <span class="key">model</span>: {
+ <span class="key">prop</span>: <span class="string"><span class="delimiter">'</span><span class="content">vehicle</span><span class="delimiter">'</span></span>, <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="key">event</span>: <span class="string"><span class="delimiter">'</span><span class="content">change</span><span class="delimiter">'</span></span>
+ },
+ <span class="key">components</span>: {
+ VehicleFormHeader,
+ FieldSelect
+ },
+ <span class="key">methods</span>: {
+ submit () { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="local-variable">this</span>.<span class="predefined">$emit</span>(<span class="string"><span class="delimiter">'</span><span class="content">submit</span><span class="delimiter">'</span></span>)
+ }
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1095,7 +1381,19 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">garage</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><app-header></span><span class="tag"></app-header></span>
+ <span class="tag"><vehicle-form</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:makes</span>=<span class="string"><span class="delimiter">"</span><span class="content">makes</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:models</span>=<span class="string"><span class="delimiter">"</span><span class="content">models</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:drivers</span>=<span class="string"><span class="delimiter">"</span><span class="content">drivers</span><span class="delimiter">"</span></span>
+ <span class="error">@</span><span class="attribute-name">submit</span>=<span class="string"><span class="delimiter">"</span><span class="content">submitNewVehicle()</span><span class="delimiter">"</span></span><span class="tag">></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="tag"></vehicle-form></span>
+ <span class="tag"><vehicle-table</span> <span class="attribute-name">:vehicles</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicles</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></vehicle-table></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="tag"></div></span>
+<span class="tag"></template></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1113,7 +1411,27 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> AppHeader from <span class="string"><span class="delimiter">'</span><span class="content">./AppHeader</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+<span class="reserved">import</span> VehicleForm from <span class="string"><span class="delimiter">'</span><span class="content">./form/VehicleForm</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> VehicleTable from <span class="string"><span class="delimiter">'</span><span class="content">./table/VehicleTable</span><span class="delimiter">'</span></span>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">components</span>: { <i class="conum" data-value="1"></i><b>(1)</b>
+ AppHeader,
+ VehicleForm,
+ VehicleTable
+ },
+ <span class="function">data</span>: <span class="keyword">function</span> () { <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="keyword">return</span> {
+ <span class="key">vehicles</span>: [],
+ <span class="key">vehicle</span>: {<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>, <span class="key">make</span>: <span class="predefined-constant">null</span>, <span class="key">model</span>: <span class="predefined-constant">null</span>, <span class="key">driver</span>: <span class="predefined-constant">null</span>},
+ <span class="key">models</span>: [],
+ <span class="key">makes</span>: [],
+ <span class="key">drivers</span>: [],
+ <span class="key">serverURL</span>: process.env.SERVER_URL <i class="conum" data-value="3"></i><b>(3)</b>
+ }
+ },</span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1135,7 +1453,23 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> created () { <i class="conum" data-value="1"></i><b>(1)</b>
+ this.fetchData()
+ },
+ methods: {
+ fetchData: async function () { <i class="conum" data-value="2"></i><b>(2)</b>
+ try {
+ Promise.all([<i class="conum" data-value="3"></i><b>(3)</b>
+ this.fetchVehicles(),
+ this.fetchModels(),
+ this.fetchModels(),
+ this.fetchMakes(),
+ this.fetchDrivers()
+ ])
+ } catch (error) {
+ console.log(error)
+ }
+ },</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1157,7 +1491,30 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> fetchVehicles: function () { <i class="conum" data-value="1"></i><b>(1)</b>
+ fetch(`${this.serverURL}/vehicle`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.vehicles = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving vehicles: ' + error))
+ },
+ fetchModels: function () {
+ fetch(`${this.serverURL}/model`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.models = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving models: ' + error))
+ },
+ fetchMakes: function () {
+ fetch(`${this.serverURL}/make`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.makes = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving makes: ' + error))
+ },
+ fetchDrivers: function () {
+ fetch(`${this.serverURL}/driver`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.drivers = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving drivers: ' + error))
+ },</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1171,7 +1528,22 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> submitNewVehicle: function () {
+ const vehicle = this.vehicle <i class="conum" data-value="1"></i><b>(1)</b>
+ fetch(`${this.serverURL}/vehicle`, {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify(vehicle)
+ }).then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> {
+ this.vehicles.push(json) <i class="conum" data-value="2"></i><b>(2)</b>
+ this.vehicle = {name: '', make: null, model: null, driver: null} <i class="conum" data-value="3"></i><b>(3)</b>
+ })
+ .catch(ex =<span class="error">></span> console.error('Unable to save vehicle', ex))
+ }
+ }
+}
+<span class="tag"></script></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1193,7 +1565,15 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="comment"><!-- Per Component Custom CSS Rules --></span>
+<i class="conum" data-value="1"></i><b>(1)</b>
+<span class="tag"><style></span>
+<span class="inline"> <span class="id">#garage</span> {
+ <span class="key">font-family</span>: <span class="string"><span class="delimiter">'</span><span class="content">Avenir</span><span class="delimiter">'</span></span>, <span class="value">Helvetica</span>, <span class="value">Arial</span>, <span class="value">sans-serif</span>;
+ <span class="key">text-align</span>: <span class="value">center</span>;
+ <span class="key">color</span>: <span class="color">#2c3e50</span>;
+ }</span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1225,7 +1605,21 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/router/index.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/router/index.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> Vue from <span class="string"><span class="delimiter">'</span><span class="content">vue</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> Router from <span class="string"><span class="delimiter">'</span><span class="content">vue-router</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> Garage from <span class="string"><span class="delimiter">'</span><span class="content">@/components/Garage</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+Vue.use(Router)
+
+<span class="reserved">export</span> <span class="keyword">default</span> <span class="keyword">new</span> Router({
+ <span class="key">routes</span>: [
+ {
+ <span class="key">path</span>: <span class="string"><span class="delimiter">'</span><span class="content">/</span><span class="delimiter">'</span></span>,
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Garage</span><span class="delimiter">'</span></span>, <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">component</span>: Garage <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+ ]
+})</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-vue-app/4/guide/pages/customizingApi.html b/guides/building-a-vue-app/4/guide/pages/customizingApi.html
index 5430a402b8b..2108f3e98d5 100644
--- a/guides/building-a-vue-app/4/guide/pages/customizingApi.html
+++ b/guides/building-a-vue-app/4/guide/pages/customizingApi.html
@@ -125,7 +125,25 @@ Connection: close
<div class="listingblock">
<div class="title">server/grails-app/views/vehicle/_vehicle.gson</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/views/vehicle/_vehicle.gson[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+model {
+ Vehicle vehicle
+}
+json {
+ id vehicle.id
+
+ name vehicle.name
+
+ make <span class="key">name</span>: vehicle.make.name,
+ <span class="key">id</span>: vehicle.make.id
+
+ model <span class="key">name</span>: vehicle.model.name,
+ <span class="key">id</span>: vehicle.model.id
+
+ driver <span class="key">name</span>: vehicle.driver.name,
+ <span class="key">id</span>: vehicle.driver.id
+}</code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/building-a-vue-app/4/guide/pages/displayComponents.html b/guides/building-a-vue-app/4/guide/pages/displayComponents.html
index e5c18f2eef1..789a49edbc3 100644
--- a/guides/building-a-vue-app/4/guide/pages/displayComponents.html
+++ b/guides/building-a-vue-app/4/guide/pages/displayComponents.html
@@ -103,7 +103,38 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/form/FieldSelect.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/form/FieldSelect.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">driverSelect-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span c [...]
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-group</span><span class="delimiter">"</span></span><span class="tag">></span> <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="tag"><select</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">selected</span><span class="delimiter">"</span></span> <span class="error">@</span><span class="attribute-name">change</span>=<span class [...]
+ <span class="tag"><option</span> <span class="attribute-name">disabled</span> <span class="attribute-name">:value</span>=<span class="string"><span class="delimiter">"</span><span class="content">null</span><span class="delimiter">"</span></span><span class="tag">></span>Select a {{field}}<span class="tag"></option></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="tag"><option</span> <span class="attribute-name">v-for</span>=<span class="string"><span class="delimiter">"</span><span class="content">value in values</span><span class="delimiter">"</span></span> <span class="attribute-name">:value</span>=<span class="string"><span class="delimiter">"</span><span class="content">value.id</span><span class="delimiter">"</span></span> <span class="attribute-name">:key</span>=<span class="string"><span class="del [...]
+ {{ value.name }}
+ <span class="tag"></option></span>
+ <span class="tag"></select></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">values</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">field</span><span class="delimiter">'</span></span>], <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="function">data</span>: <span class="keyword">function</span> () { <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="keyword">return</span> {
+ <span class="key">selected</span>: <span class="predefined-constant">null</span> <i class="conum" data-value="3"></i><b>(3)</b>
+ }
+ },
+ <span class="key">methods</span>: { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="function">updateValue</span>: <span class="keyword">function</span> () { <i class="conum" data-value="6"></i><b>(6)</b>
+ <span class="local-variable">this</span>.<span class="predefined">$emit</span>(<span class="string"><span class="delimiter">'</span><span class="content">input</span><span class="delimiter">'</span></span>, <span class="local-variable">this</span>.selected)
+ }
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -171,7 +202,26 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/TableRow.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/table/TableRow.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">tablerow-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span [...]
+ <span class="tag"><tr></span> <span class="comment"><!-- 1 --></span>
+ <span class="tag"><td></span>{{ item.id }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.make.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.model.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.driver.name }}<span class="tag"></td></span>
+ <span class="tag"></tr></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">item</span><span class="delimiter">'</span></span>] <i class="conum" data-value="1"></i><b>(1)</b>
+}</span>
+<span class="tag"></script></span>
+
+<!-- Per Component Custom CSS Rules -->
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -188,7 +238,36 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/table/VehicleTable.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/table/VehicleTable.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">fulltable-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><spa [...]
+ <span class="tag"><table</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">table</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><thead</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">thead-inverse</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><tr></span>
+ <span class="tag"><th></span>ID<span class="tag"></th></span>
+ <span class="tag"><th></span>Name<span class="tag"></th></span>
+ <span class="tag"><th></span>Make<span class="tag"></th></span>
+ <span class="tag"><th></span>Model<span class="tag"></th></span>
+ <span class="tag"><th></span>Driver<span class="tag"></th></span>
+ <span class="tag"></tr></span>
+ <span class="tag"></thead></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="tag"><table-row</span> <span class="attribute-name">v-for</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle in vehicles</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:item</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle</span><span class="delimiter">"</span></span> <span class="attribute-name">:key</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.id</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></table-row></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="tag"></table></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> TableRow from <span class="string"><span class="delimiter">'</span><span class="content">./TableRow.vue</span><span class="delimiter">'</span></span> <i class="conum" data-value="3"></i><b>(3)</b>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">vehicles</span><span class="delimiter">'</span></span>],
+ <span class="key">components</span>: { <i class="conum" data-value="3"></i><b>(3)</b>
+ TableRow
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-vue-app/4/guide/pages/headers.html b/guides/building-a-vue-app/4/guide/pages/headers.html
index efac0759bfc..40a6146206d 100644
--- a/guides/building-a-vue-app/4/guide/pages/headers.html
+++ b/guides/building-a-vue-app/4/guide/pages/headers.html
@@ -93,7 +93,23 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/AppHeader.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/AppHeader.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">app-header-template</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">container</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">jumbotron</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><img</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">../assets/logo.png</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><h1></span>Welcome to the Garage<span class="tag"></h1></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">app-header</span><span class="delimiter">'</span></span>
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -123,7 +139,24 @@ Every <code><template></code> must contain only one root-level element.
<div class="listingblock">
<div class="title">/client/src/components/VehicleAddHeader.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/form/VehicleFormHeader.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">add-vehicle-header-template</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">headerRow</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><h3></span>Add a Vehicle:<span class="tag"></h3></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">vehicle-form-header</span><span class="delimiter">'</span></span>
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="admonitionblock tip">
diff --git a/guides/building-a-vue-app/4/guide/pages/oneMoreComponent.html b/guides/building-a-vue-app/4/guide/pages/oneMoreComponent.html
index 42fbbb0e00c..e1bb120a05c 100644
--- a/guides/building-a-vue-app/4/guide/pages/oneMoreComponent.html
+++ b/guides/building-a-vue-app/4/guide/pages/oneMoreComponent.html
@@ -203,7 +203,19 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">garage</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><app-header></span><span class="tag"></app-header></span>
+ <span class="tag"><vehicle-form</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:makes</span>=<span class="string"><span class="delimiter">"</span><span class="content">makes</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:models</span>=<span class="string"><span class="delimiter">"</span><span class="content">models</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:drivers</span>=<span class="string"><span class="delimiter">"</span><span class="content">drivers</span><span class="delimiter">"</span></span>
+ <span class="error">@</span><span class="attribute-name">submit</span>=<span class="string"><span class="delimiter">"</span><span class="content">submitNewVehicle()</span><span class="delimiter">"</span></span><span class="tag">></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="tag"></vehicle-form></span>
+ <span class="tag"><vehicle-table</span> <span class="attribute-name">:vehicles</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicles</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></vehicle-table></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="tag"></div></span>
+<span class="tag"></template></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -221,7 +233,27 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> AppHeader from <span class="string"><span class="delimiter">'</span><span class="content">./AppHeader</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+<span class="reserved">import</span> VehicleForm from <span class="string"><span class="delimiter">'</span><span class="content">./form/VehicleForm</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> VehicleTable from <span class="string"><span class="delimiter">'</span><span class="content">./table/VehicleTable</span><span class="delimiter">'</span></span>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">components</span>: { <i class="conum" data-value="1"></i><b>(1)</b>
+ AppHeader,
+ VehicleForm,
+ VehicleTable
+ },
+ <span class="function">data</span>: <span class="keyword">function</span> () { <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="keyword">return</span> {
+ <span class="key">vehicles</span>: [],
+ <span class="key">vehicle</span>: {<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>, <span class="key">make</span>: <span class="predefined-constant">null</span>, <span class="key">model</span>: <span class="predefined-constant">null</span>, <span class="key">driver</span>: <span class="predefined-constant">null</span>},
+ <span class="key">models</span>: [],
+ <span class="key">makes</span>: [],
+ <span class="key">drivers</span>: [],
+ <span class="key">serverURL</span>: process.env.SERVER_URL <i class="conum" data-value="3"></i><b>(3)</b>
+ }
+ },</span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -243,7 +275,23 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> created () { <i class="conum" data-value="1"></i><b>(1)</b>
+ this.fetchData()
+ },
+ methods: {
+ fetchData: async function () { <i class="conum" data-value="2"></i><b>(2)</b>
+ try {
+ Promise.all([<i class="conum" data-value="3"></i><b>(3)</b>
+ this.fetchVehicles(),
+ this.fetchModels(),
+ this.fetchModels(),
+ this.fetchMakes(),
+ this.fetchDrivers()
+ ])
+ } catch (error) {
+ console.log(error)
+ }
+ },</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -265,7 +313,30 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> fetchVehicles: function () { <i class="conum" data-value="1"></i><b>(1)</b>
+ fetch(`${this.serverURL}/vehicle`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.vehicles = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving vehicles: ' + error))
+ },
+ fetchModels: function () {
+ fetch(`${this.serverURL}/model`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.models = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving models: ' + error))
+ },
+ fetchMakes: function () {
+ fetch(`${this.serverURL}/make`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.makes = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving makes: ' + error))
+ },
+ fetchDrivers: function () {
+ fetch(`${this.serverURL}/driver`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.drivers = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving drivers: ' + error))
+ },</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -279,7 +350,22 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> submitNewVehicle: function () {
+ const vehicle = this.vehicle <i class="conum" data-value="1"></i><b>(1)</b>
+ fetch(`${this.serverURL}/vehicle`, {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify(vehicle)
+ }).then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> {
+ this.vehicles.push(json) <i class="conum" data-value="2"></i><b>(2)</b>
+ this.vehicle = {name: '', make: null, model: null, driver: null} <i class="conum" data-value="3"></i><b>(3)</b>
+ })
+ .catch(ex =<span class="error">></span> console.error('Unable to save vehicle', ex))
+ }
+ }
+}
+<span class="tag"></script></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -301,7 +387,15 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="comment"><!-- Per Component Custom CSS Rules --></span>
+<i class="conum" data-value="1"></i><b>(1)</b>
+<span class="tag"><style></span>
+<span class="inline"> <span class="id">#garage</span> {
+ <span class="key">font-family</span>: <span class="string"><span class="delimiter">'</span><span class="content">Avenir</span><span class="delimiter">'</span></span>, <span class="value">Helvetica</span>, <span class="value">Arial</span>, <span class="value">sans-serif</span>;
+ <span class="key">text-align</span>: <span class="value">center</span>;
+ <span class="key">color</span>: <span class="color">#2c3e50</span>;
+ }</span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-vue-app/4/guide/pages/postComponents.html b/guides/building-a-vue-app/4/guide/pages/postComponents.html
index 0685631afec..c7fd155acc7 100644
--- a/guides/building-a-vue-app/4/guide/pages/postComponents.html
+++ b/guides/building-a-vue-app/4/guide/pages/postComponents.html
@@ -96,7 +96,64 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/components/form/VehicleForm.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/form/VehicleForm.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">add-vehicle-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span cl [...]
+ <span class="tag"><div></span>
+ <span class="tag"><vehicle-form-header</span><span class="tag">/></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">inputRow</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-3</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">input-group</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><input</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">placeholder</span>=<span class="string"><span class="de [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-7</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.make</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Make'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"><spa [...]
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.model</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Model'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"><s [...]
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.driver</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Driver'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"> [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-2</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn-group</span><span class="delimiter">"</span></span> <span class="attribute-name">role</span>=<span class="string"><span class="delimiter">"</span><span class="content">group</span><span class="delimiter">"</span></span> <span class="attribute-name">aria-label</span>=<span class="string"><span class="delimiter [...]
+ <span class="tag"><button</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">button</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn btn-success</span><span class="delimiter">"</span></span> <span class="error">@</span><span class="attribute-name">click</span>=<span cl [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> VehicleFormHeader from <span class="string"><span class="delimiter">'</span><span class="content">./VehicleFormHeader</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> FieldSelect from <span class="string"><span class="delimiter">'</span><span class="content">./FieldSelect</span><span class="delimiter">'</span></span>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">vehicle</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">makes</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">models</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="cont [...]
+ <span class="key">model</span>: {
+ <span class="key">prop</span>: <span class="string"><span class="delimiter">'</span><span class="content">vehicle</span><span class="delimiter">'</span></span>, <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="key">event</span>: <span class="string"><span class="delimiter">'</span><span class="content">change</span><span class="delimiter">'</span></span>
+ },
+ <span class="key">components</span>: {
+ VehicleFormHeader,
+ FieldSelect
+ },
+ <span class="key">methods</span>: {
+ submit () { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="local-variable">this</span>.<span class="predefined">$emit</span>(<span class="string"><span class="delimiter">'</span><span class="content">submit</span><span class="delimiter">'</span></span>)
+ }
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-vue-app/4/guide/pages/puttingItAllTogether.html b/guides/building-a-vue-app/4/guide/pages/puttingItAllTogether.html
index 1ac37081332..f13c823a6c9 100644
--- a/guides/building-a-vue-app/4/guide/pages/puttingItAllTogether.html
+++ b/guides/building-a-vue-app/4/guide/pages/puttingItAllTogether.html
@@ -96,7 +96,21 @@ function addJsClass() {
<div class="listingblock">
<div class="title">/client/src/router/index.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/router/index.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> Vue from <span class="string"><span class="delimiter">'</span><span class="content">vue</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> Router from <span class="string"><span class="delimiter">'</span><span class="content">vue-router</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> Garage from <span class="string"><span class="delimiter">'</span><span class="content">@/components/Garage</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+Vue.use(Router)
+
+<span class="reserved">export</span> <span class="keyword">default</span> <span class="keyword">new</span> Router({
+ <span class="key">routes</span>: [
+ {
+ <span class="key">path</span>: <span class="string"><span class="delimiter">'</span><span class="content">/</span><span class="delimiter">'</span></span>,
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Garage</span><span class="delimiter">'</span></span>, <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">component</span>: Garage <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+ ]
+})</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-vue-app/4/guide/single.html b/guides/building-a-vue-app/4/guide/single.html
index 0efe9eba6a9..f872da71aa0 100644
--- a/guides/building-a-vue-app/4/guide/single.html
+++ b/guides/building-a-vue-app/4/guide/single.html
@@ -352,25 +352,66 @@ $ grails create-domain-class demo.Model</code></pre>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Vehicle.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/Vehicle.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/vehicle</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Vehicle</span> {
+
+ <span class="predefined-type">String</span> name
+
+ Make make
+ Model model
+
+ <span class="directive">static</span> belongsTo = [<span class="key">driver</span>: <span class="predefined-type">Driver</span>]
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Driver.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/<span class="predefined-type">Driver</span>.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/driver</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Driver</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> hasMany = [ <span class="key">vehicles</span>: Vehicle ]
+
+ <span class="directive">static</span> constraints = {
+ vehicles <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Make.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/Make.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/make</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Make</span> {
+ <span class="predefined-type">String</span> name
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Model.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/Model.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/model</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Model</span> {
+ <span class="predefined-type">String</span> name
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -381,31 +422,89 @@ Let’s preload some data with the help of
<div class="listingblock">
<div class="title">server/grails-app/services/demo/MakeDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/MakeDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(Make)
+<span class="type">interface</span> MakeDataService {
+ Make save(<span class="predefined-type">String</span> name)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/services/demo/ModelDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/ModelDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(Model)
+<span class="type">interface</span> ModelDataService {
+ Model save(<span class="predefined-type">String</span> name)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/services/demo/DriverDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/DriverDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(<span class="predefined-type">Driver</span>)
+<span class="type">interface</span> DriverDataService {
+ <span class="predefined-type">Driver</span> save(<span class="predefined-type">String</span> name)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/services/demo/VehicleDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/VehicleDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(Vehicle)
+<span class="type">interface</span> VehicleDataService {
+ Vehicle save(<span class="predefined-type">String</span> name, <span class="predefined-type">Driver</span> driver, Make make, Model model)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/init/demo/BootStrap.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/init/demo/BootStrap.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">groovy.transform.CompileStatic</span>
+
+<span class="annotation">@CompileStatic</span>
+<span class="type">class</span> <span class="class">BootStrap</span> {
+
+ DriverDataService driverDataService
+ MakeDataService makeDataService
+ ModelDataService modelDataService
+ VehicleDataService vehicleDataService
+ <span class="keyword">def</span> init = { servletContext ->
+ log.info <span class="string"><span class="delimiter">"</span><span class="content">Loading database...</span><span class="delimiter">"</span></span>
+ <span class="predefined-type">Driver</span> driver1 = driverDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Susan</span><span class="delimiter">"</span></span>)
+ <span class="predefined-type">Driver</span> driver2 = driverDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Pedro</span><span class="delimiter">"</span></span>)
+
+ Make nissan = makeDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Nissan</span><span class="delimiter">"</span></span>)
+ Make ford = makeDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Ford</span><span class="delimiter">"</span></span>)
+
+ Model titan = modelDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Titan</span><span class="delimiter">"</span></span>)
+ Model leaf = modelDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Leaf</span><span class="delimiter">"</span></span>)
+ Model windstar = modelDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Windstar</span><span class="delimiter">"</span></span>)
+
+ vehicleDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Pickup</span><span class="delimiter">"</span></span>, driver1, nissan, titan)
+ vehicleDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Economy</span><span class="delimiter">"</span></span>, driver1, nissan, leaf)
+ vehicleDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Minivan</span><span class="delimiter">"</span></span>, driver2, ford, windstar)
+
+ }
+ <span class="keyword">def</span> destroy = {
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -563,7 +662,25 @@ Connection: close
<div class="listingblock">
<div class="title">server/grails-app/views/vehicle/_vehicle.gson</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/views/vehicle/_vehicle.gson[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+model {
+ Vehicle vehicle
+}
+json {
+ id vehicle.id
+
+ name vehicle.name
+
+ make <span class="key">name</span>: vehicle.make.name,
+ <span class="key">id</span>: vehicle.make.id
+
+ model <span class="key">name</span>: vehicle.model.name,
+ <span class="key">id</span>: vehicle.model.id
+
+ driver <span class="key">name</span>: vehicle.driver.name,
+ <span class="key">id</span>: vehicle.driver.id
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -700,7 +817,23 @@ While they serve a similar function, please remember that the Vue/webpack enviro
<div class="listingblock">
<div class="title">/client/src/components/AppHeader.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/AppHeader.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">app-header-template</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">container</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">jumbotron</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><img</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">../assets/logo.png</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><h1></span>Welcome to the Garage<span class="tag"></h1></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">app-header</span><span class="delimiter">'</span></span>
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -730,7 +863,24 @@ Every <code><template></code> must contain only one root-level element.
<div class="listingblock">
<div class="title">/client/src/components/VehicleAddHeader.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/form/VehicleFormHeader.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">add-vehicle-header-template</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">headerRow</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><h3></span>Add a Vehicle:<span class="tag"></h3></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">vehicle-form-header</span><span class="delimiter">'</span></span>
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="admonitionblock tip">
@@ -772,7 +922,38 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/form/FieldSelect.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/form/FieldSelect.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">driverSelect-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span c [...]
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-group</span><span class="delimiter">"</span></span><span class="tag">></span> <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="tag"><select</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">selected</span><span class="delimiter">"</span></span> <span class="error">@</span><span class="attribute-name">change</span>=<span class [...]
+ <span class="tag"><option</span> <span class="attribute-name">disabled</span> <span class="attribute-name">:value</span>=<span class="string"><span class="delimiter">"</span><span class="content">null</span><span class="delimiter">"</span></span><span class="tag">></span>Select a {{field}}<span class="tag"></option></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="tag"><option</span> <span class="attribute-name">v-for</span>=<span class="string"><span class="delimiter">"</span><span class="content">value in values</span><span class="delimiter">"</span></span> <span class="attribute-name">:value</span>=<span class="string"><span class="delimiter">"</span><span class="content">value.id</span><span class="delimiter">"</span></span> <span class="attribute-name">:key</span>=<span class="string"><span class="del [...]
+ {{ value.name }}
+ <span class="tag"></option></span>
+ <span class="tag"></select></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">values</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">field</span><span class="delimiter">'</span></span>], <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="function">data</span>: <span class="keyword">function</span> () { <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="keyword">return</span> {
+ <span class="key">selected</span>: <span class="predefined-constant">null</span> <i class="conum" data-value="3"></i><b>(3)</b>
+ }
+ },
+ <span class="key">methods</span>: { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="function">updateValue</span>: <span class="keyword">function</span> () { <i class="conum" data-value="6"></i><b>(6)</b>
+ <span class="local-variable">this</span>.<span class="predefined">$emit</span>(<span class="string"><span class="delimiter">'</span><span class="content">input</span><span class="delimiter">'</span></span>, <span class="local-variable">this</span>.selected)
+ }
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -840,7 +1021,26 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/TableRow.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/table/TableRow.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">tablerow-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span [...]
+ <span class="tag"><tr></span> <span class="comment"><!-- 1 --></span>
+ <span class="tag"><td></span>{{ item.id }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.make.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.model.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.driver.name }}<span class="tag"></td></span>
+ <span class="tag"></tr></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">item</span><span class="delimiter">'</span></span>] <i class="conum" data-value="1"></i><b>(1)</b>
+}</span>
+<span class="tag"></script></span>
+
+<!-- Per Component Custom CSS Rules -->
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -857,7 +1057,36 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/table/VehicleTable.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/table/VehicleTable.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">fulltable-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><spa [...]
+ <span class="tag"><table</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">table</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><thead</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">thead-inverse</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><tr></span>
+ <span class="tag"><th></span>ID<span class="tag"></th></span>
+ <span class="tag"><th></span>Name<span class="tag"></th></span>
+ <span class="tag"><th></span>Make<span class="tag"></th></span>
+ <span class="tag"><th></span>Model<span class="tag"></th></span>
+ <span class="tag"><th></span>Driver<span class="tag"></th></span>
+ <span class="tag"></tr></span>
+ <span class="tag"></thead></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="tag"><table-row</span> <span class="attribute-name">v-for</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle in vehicles</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:item</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle</span><span class="delimiter">"</span></span> <span class="attribute-name">:key</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.id</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></table-row></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="tag"></table></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> TableRow from <span class="string"><span class="delimiter">'</span><span class="content">./TableRow.vue</span><span class="delimiter">'</span></span> <i class="conum" data-value="3"></i><b>(3)</b>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">vehicles</span><span class="delimiter">'</span></span>],
+ <span class="key">components</span>: { <i class="conum" data-value="3"></i><b>(3)</b>
+ TableRow
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -931,7 +1160,64 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/form/VehicleForm.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/form/VehicleForm.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">add-vehicle-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span cl [...]
+ <span class="tag"><div></span>
+ <span class="tag"><vehicle-form-header</span><span class="tag">/></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">inputRow</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-3</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">input-group</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><input</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">placeholder</span>=<span class="string"><span class="de [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-7</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.make</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Make'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"><spa [...]
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.model</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Model'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"><s [...]
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.driver</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Driver'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"> [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-2</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn-group</span><span class="delimiter">"</span></span> <span class="attribute-name">role</span>=<span class="string"><span class="delimiter">"</span><span class="content">group</span><span class="delimiter">"</span></span> <span class="attribute-name">aria-label</span>=<span class="string"><span class="delimiter [...]
+ <span class="tag"><button</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">button</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn btn-success</span><span class="delimiter">"</span></span> <span class="error">@</span><span class="attribute-name">click</span>=<span cl [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> VehicleFormHeader from <span class="string"><span class="delimiter">'</span><span class="content">./VehicleFormHeader</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> FieldSelect from <span class="string"><span class="delimiter">'</span><span class="content">./FieldSelect</span><span class="delimiter">'</span></span>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">vehicle</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">makes</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">models</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="cont [...]
+ <span class="key">model</span>: {
+ <span class="key">prop</span>: <span class="string"><span class="delimiter">'</span><span class="content">vehicle</span><span class="delimiter">'</span></span>, <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="key">event</span>: <span class="string"><span class="delimiter">'</span><span class="content">change</span><span class="delimiter">'</span></span>
+ },
+ <span class="key">components</span>: {
+ VehicleFormHeader,
+ FieldSelect
+ },
+ <span class="key">methods</span>: {
+ submit () { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="local-variable">this</span>.<span class="predefined">$emit</span>(<span class="string"><span class="delimiter">'</span><span class="content">submit</span><span class="delimiter">'</span></span>)
+ }
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1095,7 +1381,19 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">garage</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><app-header></span><span class="tag"></app-header></span>
+ <span class="tag"><vehicle-form</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:makes</span>=<span class="string"><span class="delimiter">"</span><span class="content">makes</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:models</span>=<span class="string"><span class="delimiter">"</span><span class="content">models</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:drivers</span>=<span class="string"><span class="delimiter">"</span><span class="content">drivers</span><span class="delimiter">"</span></span>
+ <span class="error">@</span><span class="attribute-name">submit</span>=<span class="string"><span class="delimiter">"</span><span class="content">submitNewVehicle()</span><span class="delimiter">"</span></span><span class="tag">></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="tag"></vehicle-form></span>
+ <span class="tag"><vehicle-table</span> <span class="attribute-name">:vehicles</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicles</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></vehicle-table></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="tag"></div></span>
+<span class="tag"></template></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1113,7 +1411,27 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> AppHeader from <span class="string"><span class="delimiter">'</span><span class="content">./AppHeader</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+<span class="reserved">import</span> VehicleForm from <span class="string"><span class="delimiter">'</span><span class="content">./form/VehicleForm</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> VehicleTable from <span class="string"><span class="delimiter">'</span><span class="content">./table/VehicleTable</span><span class="delimiter">'</span></span>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">components</span>: { <i class="conum" data-value="1"></i><b>(1)</b>
+ AppHeader,
+ VehicleForm,
+ VehicleTable
+ },
+ <span class="function">data</span>: <span class="keyword">function</span> () { <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="keyword">return</span> {
+ <span class="key">vehicles</span>: [],
+ <span class="key">vehicle</span>: {<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>, <span class="key">make</span>: <span class="predefined-constant">null</span>, <span class="key">model</span>: <span class="predefined-constant">null</span>, <span class="key">driver</span>: <span class="predefined-constant">null</span>},
+ <span class="key">models</span>: [],
+ <span class="key">makes</span>: [],
+ <span class="key">drivers</span>: [],
+ <span class="key">serverURL</span>: process.env.SERVER_URL <i class="conum" data-value="3"></i><b>(3)</b>
+ }
+ },</span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1135,7 +1453,23 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> created () { <i class="conum" data-value="1"></i><b>(1)</b>
+ this.fetchData()
+ },
+ methods: {
+ fetchData: async function () { <i class="conum" data-value="2"></i><b>(2)</b>
+ try {
+ Promise.all([<i class="conum" data-value="3"></i><b>(3)</b>
+ this.fetchVehicles(),
+ this.fetchModels(),
+ this.fetchModels(),
+ this.fetchMakes(),
+ this.fetchDrivers()
+ ])
+ } catch (error) {
+ console.log(error)
+ }
+ },</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1157,7 +1491,30 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> fetchVehicles: function () { <i class="conum" data-value="1"></i><b>(1)</b>
+ fetch(`${this.serverURL}/vehicle`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.vehicles = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving vehicles: ' + error))
+ },
+ fetchModels: function () {
+ fetch(`${this.serverURL}/model`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.models = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving models: ' + error))
+ },
+ fetchMakes: function () {
+ fetch(`${this.serverURL}/make`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.makes = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving makes: ' + error))
+ },
+ fetchDrivers: function () {
+ fetch(`${this.serverURL}/driver`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.drivers = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving drivers: ' + error))
+ },</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1171,7 +1528,22 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> submitNewVehicle: function () {
+ const vehicle = this.vehicle <i class="conum" data-value="1"></i><b>(1)</b>
+ fetch(`${this.serverURL}/vehicle`, {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify(vehicle)
+ }).then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> {
+ this.vehicles.push(json) <i class="conum" data-value="2"></i><b>(2)</b>
+ this.vehicle = {name: '', make: null, model: null, driver: null} <i class="conum" data-value="3"></i><b>(3)</b>
+ })
+ .catch(ex =<span class="error">></span> console.error('Unable to save vehicle', ex))
+ }
+ }
+}
+<span class="tag"></script></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1193,7 +1565,15 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="comment"><!-- Per Component Custom CSS Rules --></span>
+<i class="conum" data-value="1"></i><b>(1)</b>
+<span class="tag"><style></span>
+<span class="inline"> <span class="id">#garage</span> {
+ <span class="key">font-family</span>: <span class="string"><span class="delimiter">'</span><span class="content">Avenir</span><span class="delimiter">'</span></span>, <span class="value">Helvetica</span>, <span class="value">Arial</span>, <span class="value">sans-serif</span>;
+ <span class="key">text-align</span>: <span class="value">center</span>;
+ <span class="key">color</span>: <span class="color">#2c3e50</span>;
+ }</span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1225,7 +1605,21 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/router/index.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/router/index.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> Vue from <span class="string"><span class="delimiter">'</span><span class="content">vue</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> Router from <span class="string"><span class="delimiter">'</span><span class="content">vue-router</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> Garage from <span class="string"><span class="delimiter">'</span><span class="content">@/components/Garage</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+Vue.use(Router)
+
+<span class="reserved">export</span> <span class="keyword">default</span> <span class="keyword">new</span> Router({
+ <span class="key">routes</span>: [
+ {
+ <span class="key">path</span>: <span class="string"><span class="delimiter">'</span><span class="content">/</span><span class="delimiter">'</span></span>,
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Garage</span><span class="delimiter">'</span></span>, <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">component</span>: Garage <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+ ]
+})</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-vue-app/4/guide/writingTheClient.html b/guides/building-a-vue-app/4/guide/writingTheClient.html
index 0f56c862fb1..647d09d276c 100644
--- a/guides/building-a-vue-app/4/guide/writingTheClient.html
+++ b/guides/building-a-vue-app/4/guide/writingTheClient.html
@@ -221,7 +221,23 @@ While they serve a similar function, please remember that the Vue/webpack enviro
<div class="listingblock">
<div class="title">/client/src/components/AppHeader.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/AppHeader.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">app-header-template</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">container</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">jumbotron</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><img</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">../assets/logo.png</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><h1></span>Welcome to the Garage<span class="tag"></h1></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">app-header</span><span class="delimiter">'</span></span>
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="paragraph">
@@ -251,7 +267,24 @@ Every <code><template></code> must contain only one root-level element.
<div class="listingblock">
<div class="title">/client/src/components/VehicleAddHeader.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/form/VehicleFormHeader.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">add-vehicle-header-template</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">headerRow</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><h3></span>Add a Vehicle:<span class="tag"></h3></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></div></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">vehicle-form-header</span><span class="delimiter">'</span></span>
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="admonitionblock tip">
@@ -293,7 +326,38 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/form/FieldSelect.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/form/FieldSelect.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">driverSelect-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span c [...]
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-group</span><span class="delimiter">"</span></span><span class="tag">></span> <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="tag"><select</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">selected</span><span class="delimiter">"</span></span> <span class="error">@</span><span class="attribute-name">change</span>=<span class [...]
+ <span class="tag"><option</span> <span class="attribute-name">disabled</span> <span class="attribute-name">:value</span>=<span class="string"><span class="delimiter">"</span><span class="content">null</span><span class="delimiter">"</span></span><span class="tag">></span>Select a {{field}}<span class="tag"></option></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="tag"><option</span> <span class="attribute-name">v-for</span>=<span class="string"><span class="delimiter">"</span><span class="content">value in values</span><span class="delimiter">"</span></span> <span class="attribute-name">:value</span>=<span class="string"><span class="delimiter">"</span><span class="content">value.id</span><span class="delimiter">"</span></span> <span class="attribute-name">:key</span>=<span class="string"><span class="del [...]
+ {{ value.name }}
+ <span class="tag"></option></span>
+ <span class="tag"></select></span>
+ <span class="tag"></div></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">values</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">field</span><span class="delimiter">'</span></span>], <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="function">data</span>: <span class="keyword">function</span> () { <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="keyword">return</span> {
+ <span class="key">selected</span>: <span class="predefined-constant">null</span> <i class="conum" data-value="3"></i><b>(3)</b>
+ }
+ },
+ <span class="key">methods</span>: { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="function">updateValue</span>: <span class="keyword">function</span> () { <i class="conum" data-value="6"></i><b>(6)</b>
+ <span class="local-variable">this</span>.<span class="predefined">$emit</span>(<span class="string"><span class="delimiter">'</span><span class="content">input</span><span class="delimiter">'</span></span>, <span class="local-variable">this</span>.selected)
+ }
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -361,7 +425,26 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/TableRow.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/table/TableRow.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">tablerow-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span [...]
+ <span class="tag"><tr></span> <span class="comment"><!-- 1 --></span>
+ <span class="tag"><td></span>{{ item.id }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.make.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.model.name }}<span class="tag"></td></span>
+ <span class="tag"><td></span>{{ item.driver.name }}<span class="tag"></td></span>
+ <span class="tag"></tr></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">item</span><span class="delimiter">'</span></span>] <i class="conum" data-value="1"></i><b>(1)</b>
+}</span>
+<span class="tag"></script></span>
+
+<!-- Per Component Custom CSS Rules -->
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -378,7 +461,36 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/table/VehicleTable.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/components/table/VehicleTable.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">fulltable-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><spa [...]
+ <span class="tag"><table</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">table</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><thead</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">thead-inverse</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><tr></span>
+ <span class="tag"><th></span>ID<span class="tag"></th></span>
+ <span class="tag"><th></span>Name<span class="tag"></th></span>
+ <span class="tag"><th></span>Make<span class="tag"></th></span>
+ <span class="tag"><th></span>Model<span class="tag"></th></span>
+ <span class="tag"><th></span>Driver<span class="tag"></th></span>
+ <span class="tag"></tr></span>
+ <span class="tag"></thead></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="tag"><table-row</span> <span class="attribute-name">v-for</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle in vehicles</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:item</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle</span><span class="delimiter">"</span></span> <span class="attribute-name">:key</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.id</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></table-row></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="tag"></table></span>
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> TableRow from <span class="string"><span class="delimiter">'</span><span class="content">./TableRow.vue</span><span class="delimiter">'</span></span> <i class="conum" data-value="3"></i><b>(3)</b>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">vehicles</span><span class="delimiter">'</span></span>],
+ <span class="key">components</span>: { <i class="conum" data-value="3"></i><b>(3)</b>
+ TableRow
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -452,7 +564,64 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/form/VehicleForm.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/form/VehicleForm.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">add-vehicle-template</span><span class="delimiter">"</span></span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span><span cl [...]
+ <span class="tag"><div></span>
+ <span class="tag"><vehicle-form-header</span><span class="tag">/></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">inputRow</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-3</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">input-group</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><input</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">form-control</span><span class="delimiter">"</span></span> <span class="attribute-name">placeholder</span>=<span class="string"><span class="de [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-7</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">row</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.make</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Make'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"><spa [...]
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.model</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Model'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"><s [...]
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-4</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><field-select</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle.driver</span><span class="delimiter">"</span></span> <span class="attribute-name">:field</span>=<span class="string"><span class="delimiter">"</span><span class="content">'Driver'</span><span class="delimiter">"</span></span> <span class="attribute-name">:values</span>=<span class="string"> [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">col-sm-2</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn-group</span><span class="delimiter">"</span></span> <span class="attribute-name">role</span>=<span class="string"><span class="delimiter">"</span><span class="content">group</span><span class="delimiter">"</span></span> <span class="attribute-name">aria-label</span>=<span class="string"><span class="delimiter [...]
+ <span class="tag"><button</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">button</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">btn btn-success</span><span class="delimiter">"</span></span> <span class="error">@</span><span class="attribute-name">click</span>=<span cl [...]
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+ <span class="tag"></div></span>
+
+<span class="tag"></template></span>
+
+<span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> VehicleFormHeader from <span class="string"><span class="delimiter">'</span><span class="content">./VehicleFormHeader</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> FieldSelect from <span class="string"><span class="delimiter">'</span><span class="content">./FieldSelect</span><span class="delimiter">'</span></span>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">props</span>: [<span class="string"><span class="delimiter">'</span><span class="content">vehicle</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">makes</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">models</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="cont [...]
+ <span class="key">model</span>: {
+ <span class="key">prop</span>: <span class="string"><span class="delimiter">'</span><span class="content">vehicle</span><span class="delimiter">'</span></span>, <i class="conum" data-value="4"></i><b>(4)</b>
+ <span class="key">event</span>: <span class="string"><span class="delimiter">'</span><span class="content">change</span><span class="delimiter">'</span></span>
+ },
+ <span class="key">components</span>: {
+ VehicleFormHeader,
+ FieldSelect
+ },
+ <span class="key">methods</span>: {
+ submit () { <i class="conum" data-value="5"></i><b>(5)</b>
+ <span class="local-variable">this</span>.<span class="predefined">$emit</span>(<span class="string"><span class="delimiter">'</span><span class="content">submit</span><span class="delimiter">'</span></span>)
+ }
+ }
+}</span>
+<span class="tag"></script></span>
+
+<span class="tag"><style></span>
+<span class="inline"> <span class="comment">/* Add custom rules here */</span></span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -616,7 +785,19 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><template></span>
+ <span class="tag"><div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">garage</span><span class="delimiter">"</span></span><span class="tag">></span>
+ <span class="tag"><app-header></span><span class="tag"></app-header></span>
+ <span class="tag"><vehicle-form</span> <span class="attribute-name">v-model</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicle</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:makes</span>=<span class="string"><span class="delimiter">"</span><span class="content">makes</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:models</span>=<span class="string"><span class="delimiter">"</span><span class="content">models</span><span class="delimiter">"</span></span>
+ <span class="attribute-name">:drivers</span>=<span class="string"><span class="delimiter">"</span><span class="content">drivers</span><span class="delimiter">"</span></span>
+ <span class="error">@</span><span class="attribute-name">submit</span>=<span class="string"><span class="delimiter">"</span><span class="content">submitNewVehicle()</span><span class="delimiter">"</span></span><span class="tag">></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="tag"></vehicle-form></span>
+ <span class="tag"><vehicle-table</span> <span class="attribute-name">:vehicles</span>=<span class="string"><span class="delimiter">"</span><span class="content">vehicles</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></vehicle-table></span> <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="tag"></div></span>
+<span class="tag"></template></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -634,7 +815,27 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="tag"><script></span>
+<span class="inline"><span class="reserved">import</span> AppHeader from <span class="string"><span class="delimiter">'</span><span class="content">./AppHeader</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+<span class="reserved">import</span> VehicleForm from <span class="string"><span class="delimiter">'</span><span class="content">./form/VehicleForm</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> VehicleTable from <span class="string"><span class="delimiter">'</span><span class="content">./table/VehicleTable</span><span class="delimiter">'</span></span>
+
+<span class="reserved">export</span> <span class="keyword">default</span> {
+ <span class="key">components</span>: { <i class="conum" data-value="1"></i><b>(1)</b>
+ AppHeader,
+ VehicleForm,
+ VehicleTable
+ },
+ <span class="function">data</span>: <span class="keyword">function</span> () { <i class="conum" data-value="2"></i><b>(2)</b>
+ <span class="keyword">return</span> {
+ <span class="key">vehicles</span>: [],
+ <span class="key">vehicle</span>: {<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>, <span class="key">make</span>: <span class="predefined-constant">null</span>, <span class="key">model</span>: <span class="predefined-constant">null</span>, <span class="key">driver</span>: <span class="predefined-constant">null</span>},
+ <span class="key">models</span>: [],
+ <span class="key">makes</span>: [],
+ <span class="key">drivers</span>: [],
+ <span class="key">serverURL</span>: process.env.SERVER_URL <i class="conum" data-value="3"></i><b>(3)</b>
+ }
+ },</span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -656,7 +857,23 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> created () { <i class="conum" data-value="1"></i><b>(1)</b>
+ this.fetchData()
+ },
+ methods: {
+ fetchData: async function () { <i class="conum" data-value="2"></i><b>(2)</b>
+ try {
+ Promise.all([<i class="conum" data-value="3"></i><b>(3)</b>
+ this.fetchVehicles(),
+ this.fetchModels(),
+ this.fetchModels(),
+ this.fetchMakes(),
+ this.fetchDrivers()
+ ])
+ } catch (error) {
+ console.log(error)
+ }
+ },</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -678,7 +895,30 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> fetchVehicles: function () { <i class="conum" data-value="1"></i><b>(1)</b>
+ fetch(`${this.serverURL}/vehicle`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.vehicles = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving vehicles: ' + error))
+ },
+ fetchModels: function () {
+ fetch(`${this.serverURL}/model`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.models = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving models: ' + error))
+ },
+ fetchMakes: function () {
+ fetch(`${this.serverURL}/make`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.makes = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving makes: ' + error))
+ },
+ fetchDrivers: function () {
+ fetch(`${this.serverURL}/driver`)
+ .then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> { this.drivers = json })
+ .catch(error =<span class="error">></span> console.error('Error retrieving drivers: ' + error))
+ },</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -692,7 +932,22 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"> submitNewVehicle: function () {
+ const vehicle = this.vehicle <i class="conum" data-value="1"></i><b>(1)</b>
+ fetch(`${this.serverURL}/vehicle`, {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify(vehicle)
+ }).then(r =<span class="error">></span> r.json())
+ .then(json =<span class="error">></span> {
+ this.vehicles.push(json) <i class="conum" data-value="2"></i><b>(2)</b>
+ this.vehicle = {name: '', make: null, model: null, driver: null} <i class="conum" data-value="3"></i><b>(3)</b>
+ })
+ .catch(ex =<span class="error">></span> console.error('Unable to save vehicle', ex))
+ }
+ }
+}
+<span class="tag"></script></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -714,7 +969,15 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/components/Garage.vue</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="html">link:../snippets/client/src/components/Garage.vue[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="html"><span class="comment"><!-- Per Component Custom CSS Rules --></span>
+<i class="conum" data-value="1"></i><b>(1)</b>
+<span class="tag"><style></span>
+<span class="inline"> <span class="id">#garage</span> {
+ <span class="key">font-family</span>: <span class="string"><span class="delimiter">'</span><span class="content">Avenir</span><span class="delimiter">'</span></span>, <span class="value">Helvetica</span>, <span class="value">Arial</span>, <span class="value">sans-serif</span>;
+ <span class="key">text-align</span>: <span class="value">center</span>;
+ <span class="key">color</span>: <span class="color">#2c3e50</span>;
+ }</span>
+<span class="tag"></style></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -746,7 +1009,21 @@ By default, when you use a component in a template, the element name will be the
<div class="listingblock">
<div class="title">/client/src/router/index.js</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="javascript">link:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>client/src/router/index.js[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">import</span> Vue from <span class="string"><span class="delimiter">'</span><span class="content">vue</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> Router from <span class="string"><span class="delimiter">'</span><span class="content">vue-router</span><span class="delimiter">'</span></span>
+<span class="reserved">import</span> Garage from <span class="string"><span class="delimiter">'</span><span class="content">@/components/Garage</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+
+Vue.use(Router)
+
+<span class="reserved">export</span> <span class="keyword">default</span> <span class="keyword">new</span> Router({
+ <span class="key">routes</span>: [
+ {
+ <span class="key">path</span>: <span class="string"><span class="delimiter">'</span><span class="content">/</span><span class="delimiter">'</span></span>,
+ <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Garage</span><span class="delimiter">'</span></span>, <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="key">component</span>: Garage <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+ ]
+})</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-a-vue-app/4/guide/writingTheServer.html b/guides/building-a-vue-app/4/guide/writingTheServer.html
index 0533feb8ef5..3be61f310aa 100644
--- a/guides/building-a-vue-app/4/guide/writingTheServer.html
+++ b/guides/building-a-vue-app/4/guide/writingTheServer.html
@@ -115,25 +115,66 @@ $ grails create-domain-class demo.Model</code></pre>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Vehicle.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/Vehicle.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/vehicle</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Vehicle</span> {
+
+ <span class="predefined-type">String</span> name
+
+ Make make
+ Model model
+
+ <span class="directive">static</span> belongsTo = [<span class="key">driver</span>: <span class="predefined-type">Driver</span>]
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Driver.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/<span class="predefined-type">Driver</span>.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/driver</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Driver</span> {
+
+ <span class="predefined-type">String</span> name
+
+ <span class="directive">static</span> hasMany = [ <span class="key">vehicles</span>: Vehicle ]
+
+ <span class="directive">static</span> constraints = {
+ vehicles <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Make.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/Make.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/make</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Make</span> {
+ <span class="predefined-type">String</span> name
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/domain/demo/Model.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/domain/demo/Model.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.rest.Resource</span>
+
+<span class="annotation">@Resource</span>(uri = <span class="string"><span class="delimiter">'</span><span class="content">/model</span><span class="delimiter">'</span></span>)
+<span class="type">class</span> <span class="class">Model</span> {
+ <span class="predefined-type">String</span> name
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -144,31 +185,89 @@ Let’s preload some data with the help of
<div class="listingblock">
<div class="title">server/grails-app/services/demo/MakeDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/MakeDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(Make)
+<span class="type">interface</span> MakeDataService {
+ Make save(<span class="predefined-type">String</span> name)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/services/demo/ModelDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/ModelDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(Model)
+<span class="type">interface</span> ModelDataService {
+ Model save(<span class="predefined-type">String</span> name)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/services/demo/DriverDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/DriverDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(<span class="predefined-type">Driver</span>)
+<span class="type">interface</span> DriverDataService {
+ <span class="predefined-type">Driver</span> save(<span class="predefined-type">String</span> name)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/services/demo/VehicleDataService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/services/demo/VehicleDataService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">grails.gorm.services.Service</span>
+
+<span class="annotation">@Service</span>(Vehicle)
+<span class="type">interface</span> VehicleDataService {
+ Vehicle save(<span class="predefined-type">String</span> name, <span class="predefined-type">Driver</span> driver, Make make, Model model)
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">server/grails-app/init/demo/BootStrap.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/init/demo/BootStrap.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">groovy.transform.CompileStatic</span>
+
+<span class="annotation">@CompileStatic</span>
+<span class="type">class</span> <span class="class">BootStrap</span> {
+
+ DriverDataService driverDataService
+ MakeDataService makeDataService
+ ModelDataService modelDataService
+ VehicleDataService vehicleDataService
+ <span class="keyword">def</span> init = { servletContext ->
+ log.info <span class="string"><span class="delimiter">"</span><span class="content">Loading database...</span><span class="delimiter">"</span></span>
+ <span class="predefined-type">Driver</span> driver1 = driverDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Susan</span><span class="delimiter">"</span></span>)
+ <span class="predefined-type">Driver</span> driver2 = driverDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Pedro</span><span class="delimiter">"</span></span>)
+
+ Make nissan = makeDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Nissan</span><span class="delimiter">"</span></span>)
+ Make ford = makeDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Ford</span><span class="delimiter">"</span></span>)
+
+ Model titan = modelDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Titan</span><span class="delimiter">"</span></span>)
+ Model leaf = modelDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Leaf</span><span class="delimiter">"</span></span>)
+ Model windstar = modelDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Windstar</span><span class="delimiter">"</span></span>)
+
+ vehicleDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Pickup</span><span class="delimiter">"</span></span>, driver1, nissan, titan)
+ vehicleDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Economy</span><span class="delimiter">"</span></span>, driver1, nissan, leaf)
+ vehicleDataService.save(<span class="string"><span class="delimiter">"</span><span class="content">Minivan</span><span class="delimiter">"</span></span>, driver2, ford, windstar)
+
+ }
+ <span class="keyword">def</span> destroy = {
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -326,7 +425,25 @@ Connection: close
<div class="listingblock">
<div class="title">server/grails-app/views/vehicle/_vehicle.gson</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>server/grails-app/views/vehicle/_vehicle.gson[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">demo.Vehicle</span>
+
+model {
+ Vehicle vehicle
+}
+json {
+ id vehicle.id
+
+ name vehicle.name
+
+ make <span class="key">name</span>: vehicle.make.name,
+ <span class="key">id</span>: vehicle.make.id
+
+ model <span class="key">name</span>: vehicle.model.name,
+ <span class="key">id</span>: vehicle.model.id
+
+ driver <span class="key">name</span>: vehicle.driver.name,
+ <span class="key">id</span>: vehicle.driver.id
+}</code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/building-a-vue-app/4/img/complete.png b/guides/building-a-vue-app/4/img/complete.png
new file mode 100644
index 00000000000..03c307ffe55
Binary files /dev/null and b/guides/building-a-vue-app/4/img/complete.png differ
diff --git a/guides/building-a-vue-app/4/img/vueApp.png b/guides/building-a-vue-app/4/img/vueApp.png
new file mode 100644
index 00000000000..437b13551ac
Binary files /dev/null and b/guides/building-a-vue-app/4/img/vueApp.png differ
diff --git a/guides/building-a-vue-app/4/img/vueConfig.png b/guides/building-a-vue-app/4/img/vueConfig.png
new file mode 100644
index 00000000000..bebfa89f6be
Binary files /dev/null and b/guides/building-a-vue-app/4/img/vueConfig.png differ
diff --git a/guides/building-a-vue-app/4/img/vueDirListing.png b/guides/building-a-vue-app/4/img/vueDirListing.png
new file mode 100644
index 00000000000..22a31000933
Binary files /dev/null and b/guides/building-a-vue-app/4/img/vueDirListing.png differ
diff --git a/guides/building-a-vue-app/4/img/vueSelect.png b/guides/building-a-vue-app/4/img/vueSelect.png
new file mode 100644
index 00000000000..a328d49cb24
Binary files /dev/null and b/guides/building-a-vue-app/4/img/vueSelect.png differ
diff --git a/guides/building-a-vue-app/4/img/vueTest.png b/guides/building-a-vue-app/4/img/vueTest.png
new file mode 100644
index 00000000000..6993401c6ea
Binary files /dev/null and b/guides/building-a-vue-app/4/img/vueTest.png differ
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/index.html b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/index.html
index ef576daee0c..b08095d28fc 100644
--- a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/index.html
+++ b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/index.html
@@ -439,7 +439,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -472,7 +487,47 @@ nullability and length of both <code>title</code> and <code>body</code> properti
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -576,7 +631,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -604,7 +671,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
@@ -684,7 +751,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1035,7 +1140,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1044,7 +1169,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1053,7 +1194,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -1064,7 +1246,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
@@ -1089,7 +1272,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html
index 7380016b67a..94c2dcc8081 100644
--- a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html
+++ b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html
@@ -121,7 +121,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html
index 756e7aadf3c..344e74ea7c1 100644
--- a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html
+++ b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html
@@ -101,7 +101,47 @@ nullability and length of both <code>title</code> and <code>body</code> properti
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html
index c862d042e72..4aa456ed856 100644
--- a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html
+++ b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html
@@ -98,7 +98,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -126,7 +138,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html
index 347ac4b2039..717b956455b 100644
--- a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html
+++ b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html
@@ -104,7 +104,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html
index b3e014ff293..e638d292e71 100644
--- a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html
+++ b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html
@@ -97,7 +97,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -106,7 +126,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -115,7 +151,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -126,7 +203,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html
index f04a134e4f0..4d30eaae1da 100644
--- a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html
+++ b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html
@@ -100,7 +100,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/single.html b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/single.html
index ef576daee0c..b08095d28fc 100644
--- a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/single.html
+++ b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/single.html
@@ -439,7 +439,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -472,7 +487,47 @@ nullability and length of both <code>title</code> and <code>body</code> properti
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -576,7 +631,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -604,7 +671,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
@@ -684,7 +751,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1035,7 +1140,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1044,7 +1169,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1053,7 +1194,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -1064,7 +1246,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
@@ -1089,7 +1272,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/version2.html b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/version2.html
index 2a54c983e42..ad66459f56a 100644
--- a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/version2.html
+++ b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/version2.html
@@ -134,7 +134,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -143,7 +163,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -152,7 +188,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -163,7 +240,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
@@ -188,7 +266,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html
index 0ad93dffd6c..75998f792b7 100644
--- a/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html
+++ b/guides/building-an-android-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html
@@ -161,7 +161,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -194,7 +209,47 @@ nullability and length of both <code>title</code> and <code>body</code> properti
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -298,7 +353,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -326,7 +393,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
@@ -406,7 +473,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-announcements-overview.png b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-announcements-overview.png
new file mode 100644
index 00000000000..2586afedb4a
Binary files /dev/null and b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-announcements-overview.png differ
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject1.png b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject1.png
new file mode 100644
index 00000000000..7f4189cd118
Binary files /dev/null and b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject1.png differ
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject2.png b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject2.png
new file mode 100644
index 00000000000..824509c5ce7
Binary files /dev/null and b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject2.png differ
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject3.png b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject3.png
new file mode 100644
index 00000000000..c0e835c0b08
Binary files /dev/null and b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject3.png differ
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject4.png b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject4.png
new file mode 100644
index 00000000000..4dfd3f4b88b
Binary files /dev/null and b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/android-studio-newproject4.png differ
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/img/extras.png b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/extras.png
new file mode 100644
index 00000000000..b7b602f03de
Binary files /dev/null and b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/extras.png differ
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/img/overview.png b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/overview.png
new file mode 100644
index 00000000000..6c9c6ba4d16
Binary files /dev/null and b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/overview.png differ
diff --git a/guides/building-an-android-client-powered-by-a-grails-backend/4/img/version2overview.png b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/version2overview.png
new file mode 100644
index 00000000000..26f2d448226
Binary files /dev/null and b/guides/building-an-android-client-powered-by-a-grails-backend/4/img/version2overview.png differ
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/index.html b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/index.html
index 831510a7f63..465d04a526c 100644
--- a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/index.html
+++ b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/index.html
@@ -445,7 +445,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -478,7 +493,50 @@ nullability and length of both title and body properties.</p>
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="type">class</span> <span class="class">AnnouncementSpec</span> <span class="directive">extends</span> Specification <span class="directive">implements</span> DomainUnitTest<Announcement> {
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -582,7 +640,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -610,7 +680,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
@@ -690,7 +760,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1082,7 +1190,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1091,7 +1219,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1100,7 +1244,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -1111,7 +1296,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
@@ -1136,7 +1322,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html
index 3beb0a78f0e..903046550bc 100644
--- a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html
+++ b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html
@@ -121,7 +121,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html
index e3f89a66973..e8e00a480b8 100644
--- a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html
+++ b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html
@@ -101,7 +101,50 @@ nullability and length of both title and body properties.</p>
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="type">class</span> <span class="class">AnnouncementSpec</span> <span class="directive">extends</span> Specification <span class="directive">implements</span> DomainUnitTest<Announcement> {
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html
index 511a3d3af1c..9fc4017498e 100644
--- a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html
+++ b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html
@@ -98,7 +98,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -126,7 +138,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html
index 351d720554c..fa80b201d4b 100644
--- a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html
+++ b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html
@@ -104,7 +104,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html
index 4ed3bb828b5..db8fbc96697 100644
--- a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html
+++ b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html
@@ -97,7 +97,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -106,7 +126,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -115,7 +151,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -126,7 +203,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html
index aee7afc442e..ffb05635a5a 100644
--- a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html
+++ b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html
@@ -100,7 +100,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/single.html b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/single.html
index 831510a7f63..465d04a526c 100644
--- a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/single.html
+++ b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/single.html
@@ -445,7 +445,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -478,7 +493,50 @@ nullability and length of both title and body properties.</p>
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="type">class</span> <span class="class">AnnouncementSpec</span> <span class="directive">extends</span> Specification <span class="directive">implements</span> DomainUnitTest<Announcement> {
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -582,7 +640,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -610,7 +680,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
@@ -690,7 +760,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1082,7 +1190,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1091,7 +1219,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1100,7 +1244,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -1111,7 +1296,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
@@ -1136,7 +1322,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/version2.html b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/version2.html
index b6f214433f1..33cbd5631f8 100644
--- a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/version2.html
+++ b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/version2.html
@@ -134,7 +134,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -143,7 +163,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -152,7 +188,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -163,7 +240,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
@@ -188,7 +266,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html
index 9902defa5ec..5db073d32ae 100644
--- a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html
+++ b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html
@@ -161,7 +161,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -194,7 +209,50 @@ nullability and length of both title and body properties.</p>
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="type">class</span> <span class="class">AnnouncementSpec</span> <span class="directive">extends</span> Specification <span class="directive">implements</span> DomainUnitTest<Announcement> {
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -298,7 +356,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -326,7 +396,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
@@ -406,7 +476,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/apptransportsecurity.png b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/apptransportsecurity.png
new file mode 100644
index 00000000000..99adad53934
Binary files /dev/null and b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/apptransportsecurity.png differ
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/choose_master-detail-application.png b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/choose_master-detail-application.png
new file mode 100644
index 00000000000..d481b166378
Binary files /dev/null and b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/choose_master-detail-application.png differ
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/choose_objective_c.png b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/choose_objective_c.png
new file mode 100644
index 00000000000..91120974b0a
Binary files /dev/null and b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/choose_objective_c.png differ
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/connect_detail_iboutlets.png b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/connect_detail_iboutlets.png
new file mode 100644
index 00000000000..abaa50bb2cf
Binary files /dev/null and b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/connect_detail_iboutlets.png differ
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/create_new_project.png b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/create_new_project.png
new file mode 100644
index 00000000000..cfaf82115d3
Binary files /dev/null and b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/create_new_project.png differ
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/ios-announcements-overview.jpeg b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/ios-announcements-overview.jpeg
new file mode 100644
index 00000000000..31a6aa4362a
Binary files /dev/null and b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/ios-announcements-overview.jpeg differ
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/overview.jpeg b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/overview.jpeg
new file mode 100644
index 00000000000..40be80896d7
Binary files /dev/null and b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/overview.jpeg differ
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/segue.jpeg b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/segue.jpeg
new file mode 100644
index 00000000000..d615f32cdc3
Binary files /dev/null and b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/segue.jpeg differ
diff --git a/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/version2overview.jpeg b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/version2overview.jpeg
new file mode 100644
index 00000000000..3584a8eac8d
Binary files /dev/null and b/guides/building-an-ios-objectc-client-powered-by-a-grails-backend/4/img/version2overview.jpeg differ
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/index.html b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/index.html
index bb2252d4c1b..c21216f1161 100644
--- a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/index.html
+++ b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/index.html
@@ -444,7 +444,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -477,7 +492,50 @@ nullability and length of both title and body properties.</p>
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="type">class</span> <span class="class">AnnouncementSpec</span> <span class="directive">extends</span> Specification <span class="directive">implements</span> DomainUnitTest<Announcement> {
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -581,7 +639,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -609,7 +679,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
@@ -689,7 +759,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1002,7 +1110,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1011,7 +1139,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1020,7 +1164,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -1031,7 +1216,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
@@ -1056,7 +1242,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html
index 435d7adc207..d7fcae41159 100644
--- a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html
+++ b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/domainClass.html
@@ -121,7 +121,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html
index dfb69ad31b6..58eadf58508 100644
--- a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html
+++ b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/domainClassTest.html
@@ -101,7 +101,50 @@ nullability and length of both title and body properties.</p>
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="type">class</span> <span class="class">AnnouncementSpec</span> <span class="directive">extends</span> Specification <span class="directive">implements</span> DomainUnitTest<Announcement> {
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html
index 38d18b81601..6464c6ee30e 100644
--- a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html
+++ b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v1Controller.html
@@ -98,7 +98,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -126,7 +138,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html
index 75c23d56887..c215e30ca2b 100644
--- a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html
+++ b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v1FunctionalTests.html
@@ -104,7 +104,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html
index 81e114e79fb..a38e4fc336c 100644
--- a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html
+++ b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v2Controller.html
@@ -97,7 +97,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -106,7 +126,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -115,7 +151,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -126,7 +203,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html
index 92dadae78b1..165b5465751 100644
--- a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html
+++ b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/pages/v2FunctionalTests.html
@@ -100,7 +100,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/single.html b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/single.html
index bb2252d4c1b..c21216f1161 100644
--- a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/single.html
+++ b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/single.html
@@ -444,7 +444,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -477,7 +492,50 @@ nullability and length of both title and body properties.</p>
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="type">class</span> <span class="class">AnnouncementSpec</span> <span class="directive">extends</span> Specification <span class="directive">implements</span> DomainUnitTest<Announcement> {
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -581,7 +639,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -609,7 +679,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
@@ -689,7 +759,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
@@ -1002,7 +1110,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1011,7 +1139,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1020,7 +1164,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -1031,7 +1216,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
@@ -1056,7 +1242,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/version2.html b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/version2.html
index 971e4a81091..3f31d4a96bc 100644
--- a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/version2.html
+++ b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/version2.html
@@ -134,7 +134,27 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v2/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v2
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v2</span><span class="delimiter">'</span></span>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>]
+
+ <span class="keyword">def</span> announcementService
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+
+ <span class="keyword">def</span> <span class="function">index</span>(<span class="predefined-type">Integer</span> max) {
+ params.max = <span class="predefined-type">Math</span>.min(max ?: <span class="integer">10</span>, <span class="integer">100</span>)
+ <span class="keyword">def</span> announcements = announcementService.findAllIdAndTitleProjections(params)
+ respond announcements, <span class="key">model</span>: [(<span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>resourceName<span class="inline-delimiter">}</span></span><span class="content">Count</span><span class="delimiter">"</span></span>.toString()): countResources()]
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -143,7 +163,23 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">grails-app/services/intranet/backend/AnnouncementService.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/services/intranet/backend/AnnouncementService.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span>
+
+<span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>)
+<span class="type">class</span> <span class="class">AnnouncementService</span> {
+
+ <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> findAllIdAndTitleProjections(<span class="predefined-type">Map</span> params) {
+ <span class="keyword">def</span> c = Announcement.createCriteria()
+ <span class="keyword">def</span> announcements = c.list(params) {
+ projections {
+ property(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
+ property(<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>)
+ }
+ }.collect { [<span class="key">id</span>: <span class="local-variable">it</span>[<span class="integer">0</span>], <span class="key">title</span>: <span class="local-variable">it</span>[<span class="integer">1</span>]] } <span class="keyword">as</span> <span class="predefined-type">List</span><<span class="predefined-type">Map</span>>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -152,7 +188,48 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementServiceSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.test.hibernate.HibernateSpec</span>
+<span class="keyword">import</span> <span class="include">grails.testing.services.ServiceUnitTest</span>
+
+<span class="type">class</span> <span class="class">AnnouncementServiceSpec</span> <span class="directive">extends</span> HibernateSpec <span class="directive">implements</span> ServiceUnitTest<AnnouncementService> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test criteria query with projection returns a list of maps</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Save some announcements</span><span class="delimiter">'</span></span>
+ [<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #1: Grails Interceptors</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #2: JSON Views</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #3: Multi-Project Builds</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Grails Quickcast #4: Angular Scaffolding</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Retrieving Runtime Config Values In Grails 3</span><span class="delimiter">'</span></span>),
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="string"><span class="delimiter">'</span><span class="content">Developing Grails 3 Applications With IntelliJ IDEA</span><span class="delimiter">'</span></span>)].each {
+ <span class="local-variable">it</span>.save()
+ }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">announcements are saved</span><span class="delimiter">'</span></span>
+ Announcement.count() == <span class="integer">6</span>
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">fetching the projection</span><span class="delimiter">'</span></span>
+ <span class="keyword">def</span> resp = service.findAllIdAndTitleProjections([:])
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">there are six maps in the response</span><span class="delimiter">'</span></span>
+ resp
+ resp.size() == <span class="integer">6</span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the maps contain only id and title</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="local-variable">it</span>.keySet() == [<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>>
+ }
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non empty values</span><span class="delimiter">'</span></span>
+ resp.each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ }
+
+ }
+}</code></pre>
</div>
</div>
<div class="sect2">
@@ -163,7 +240,8 @@ query with a projection to fetch only the id and title of the announcements.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
+ get <span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="inline"><span class="inline-delimiter">$</span>id</span><span class="content">(.</span><span class="inline"><span class="inline-delimiter">$</span>format</span><span class="content">)?</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">2.0</span><span class [...]
</div>
</div>
</div>
@@ -188,7 +266,44 @@ endpoint. The next functional test verifies that behaviour.</p>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains an array of annoucements with id, title and body</span><span class="delimiter">'</span></span>
+ resp.body().each {
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.id
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.title
+ <span class="keyword">assert</span> <span class="local-variable">it</span>.body <i class="conum" data-value="4"></i><b>(4)</b>
+ }
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is NOT present in announcements json payload of Api 2.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 2.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>)
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ resp.headers.getFirst(HttpHeaders.CONTENT_TYPE).get() == <span class="string"><span class="delimiter">'</span><span class="content">application/json;charset=UTF-8</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html
index 852cf37c825..53f0b88194f 100644
--- a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html
+++ b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/guide/writingTheGrailsApp.html
@@ -161,7 +161,22 @@ We are going to modify the domain class generated in the previous step to store
<div class="listingblock">
<div class="title">grails-app/domain/intranet/backend/Announcement.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/intranet/backend/Announcement.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="type">class</span> <span class="class">Announcement</span> {
+
+ <span class="predefined-type">String</span> title
+ <span class="predefined-type">String</span> body
+
+ <span class="directive">static</span> constraints = {
+ title <span class="key">size</span>: <span class="integer">0</span>..<span class="integer">255</span>
+ body <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ }
+
+ <span class="directive">static</span> mapping = {
+ body <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">text</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -194,7 +209,50 @@ nullability and length of both title and body properties.</p>
<div class="listingblock">
<div class="title">src/test/groovy/intranet/backend/AnnouncementSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/intranet/backend/AnnouncementSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="type">class</span> <span class="class">AnnouncementSpec</span> <span class="directive">extends</span> Specification <span class="directive">implements</span> DomainUnitTest<Announcement> {
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can not be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test body can have a more than 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">body validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">body</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">body</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="type">void</span> <span class="string"><span class="delimiter">"</span><span class="content">test title can have a maximum of 255 characters</span><span class="delimiter">"</span></span>() {
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ <span class="predefined-type">String</span> str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">256</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation fails</span><span class="delimiter">'</span></span>
+ !<span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">for a string of 256 characters</span><span class="delimiter">'</span></span>
+ str = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
+ <span class="integer">255</span>.times { str += <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span> }
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">title validation passes</span><span class="delimiter">'</span></span>
+ <span class="keyword">new</span> Announcement(<span class="key">title</span>: str).validate([<span class="string"><span class="delimiter">'</span><span class="content">title</span><span class="delimiter">'</span></span>])
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -298,7 +356,19 @@ different HTTP Methods.</p>
<div class="listingblock">
<div class="title">grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/v1/AnnouncementController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend.v1
+
+<span class="keyword">import</span> <span class="include">grails.rest.RestfulController</span>
+<span class="keyword">import</span> <span class="include">intranet.backend.Announcement</span>
+
+<span class="type">class</span> <span class="class">AnnouncementController</span> <span class="directive">extends</span> RestfulController<Announcement> {
+ <span class="directive">static</span> namespace = <span class="string"><span class="delimiter">'</span><span class="content">v1</span><span class="delimiter">'</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
+ <span class="directive">static</span> responseFormats = [<span class="string"><span class="delimiter">'</span><span class="content">json</span><span class="delimiter">'</span></span>] <i class="conum" data-value="2"></i><b>(2)</b>
+
+ AnnouncementController() {
+ <span class="local-variable">super</span>(Announcement)
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -326,7 +396,7 @@ do that. Add the next line to the mappings closure:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/intranet/backend/UrlMappings.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/intranet/backend/UrlMappings.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"> get <span class="string"><span class="delimiter">"</span><span class="content">/announcements</span><span class="delimiter">"</span></span>(<span class="key">version</span>:<span class="string"><span class="delimiter">'</span><span class="content">1.0</span><span class="delimiter">'</span></span>, <span class="key">controller</span>: <span class="string"><span class="delimiter">'</span><span class="content">announc [...]
</div>
</div>
</div>
@@ -406,7 +476,45 @@ dependency is added when we create an app with the <em>rest-api</em> profile.</p
<div class="listingblock">
<div class="title">/src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/intranet/backend/AnnouncementControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> intranet.backend
+
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+<span class="keyword">import</span> <span class="include">grails.testing.spock.OnceBefore</span>
+<span class="keyword">import</span> <span class="include">grails.web.http.HttpHeaders</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpRequest</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpResponse</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.HttpStatus</span>
+<span class="keyword">import</span> <span class="include">io.micronaut.http.client.HttpClient</span>
+<span class="keyword">import</span> <span class="include">spock.lang.AutoCleanup</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Shared</span>
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">AnnouncementControllerSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="annotation">@Shared</span>
+ <span class="annotation">@AutoCleanup</span>
+ HttpClient client
+
+ <span class="annotation">@OnceBefore</span>
+ <span class="type">void</span> init() {
+ <span class="predefined-type">String</span> baseUrl = <span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">$</span>serverPort</span><span class="delimiter">"</span></span>
+ <span class="local-variable">this</span>.client = HttpClient.create(baseUrl.toURL())
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test body is present in announcements json payload of Api 1.0</span><span class="delimiter">"</span></span>() {
+ <span class="key">given</span>:
+ HttpRequest request = HttpRequest.GET(<span class="string"><span class="delimiter">"</span><span class="content">/announcements/</span><span class="delimiter">"</span></span>).header(<span class="string"><span class="delimiter">"</span><span class="content">Accept-Version</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>)
+
+ <span class="key">when</span>: <span class="string"><span class="delimiter">'</span><span class="content">Requesting announcements for version 1.0</span><span class="delimiter">'</span></span>
+ HttpResponse<<span class="predefined-type">List</span><<span class="predefined-type">Map</span>>> resp = client.toBlocking().exchange(request, <span class="predefined-type">List</span>) <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">the request was successful</span><span class="delimiter">'</span></span>
+ resp.status == HttpStatus.OK <i class="conum" data-value="3"></i><b>(3)</b>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">the response is a JSON Payload</span><span class="delimiter">'</span></span>
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">json payload contains the complete announcement</span><span class="delimiter">'</span></span></code></pre>
</div>
</div>
<div class="colist arabic">
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/choose_master-detail-application.png b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/choose_master-detail-application.png
new file mode 100644
index 00000000000..d481b166378
Binary files /dev/null and b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/choose_master-detail-application.png differ
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/choose_swift.png b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/choose_swift.png
new file mode 100644
index 00000000000..c1975a00296
Binary files /dev/null and b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/choose_swift.png differ
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/connect_detail_iboutlets.png b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/connect_detail_iboutlets.png
new file mode 100644
index 00000000000..abaa50bb2cf
Binary files /dev/null and b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/connect_detail_iboutlets.png differ
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/create_new_project.png b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/create_new_project.png
new file mode 100644
index 00000000000..cfaf82115d3
Binary files /dev/null and b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/create_new_project.png differ
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/ios-announcements-overview.jpeg b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/ios-announcements-overview.jpeg
new file mode 100644
index 00000000000..12306365fa2
Binary files /dev/null and b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/ios-announcements-overview.jpeg differ
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/overview.jpeg b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/overview.jpeg
new file mode 100644
index 00000000000..40be80896d7
Binary files /dev/null and b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/overview.jpeg differ
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/segue.jpeg b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/segue.jpeg
new file mode 100644
index 00000000000..d615f32cdc3
Binary files /dev/null and b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/segue.jpeg differ
diff --git a/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/version2overview.jpeg b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/version2overview.jpeg
new file mode 100644
index 00000000000..3584a8eac8d
Binary files /dev/null and b/guides/building-an-ios-swift-client-powered-by-a-grails-backend/4/img/version2overview.jpeg differ
diff --git a/guides/command-objects-and-forms/3/guide/index.html b/guides/command-objects-and-forms/3/guide/index.html
index 4c0c92a343a..fc299130c1b 100644
--- a/guides/command-objects-and-forms/3/guide/index.html
+++ b/guides/command-objects-and-forms/3/guide/index.html
@@ -359,7 +359,20 @@ the <code>show</code> action to look like this:</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/demo/PlayerController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/PlayerController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">org.springframework.http.HttpStatus</span>
+
+<span class="type">class</span> <span class="class">PlayerController</span> {
+
+ <span class="keyword">def</span> <span class="function">index</span>() {
+ respond Player.list(params), <span class="key">model</span>: [<span class="key">playerCount</span>: Player.count()]
+ }
+
+ <span class="keyword">def</span> <span class="function">show</span>(Player player) {
+ respond player
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -410,7 +423,10 @@ action.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/demo/PlayerController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/PlayerController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="annotation">@SuppressWarnings</span>([<span class="string"><span class="delimiter">'</span><span class="content">FactoryMethodName</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">GrailsMassAssignment</span><span class="delimiter">'</span></span>])
+<span class="keyword">def</span> <span class="function">create</span>() {
+ respond <span class="keyword">new</span> Player(params)
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -483,7 +499,23 @@ the <code><form></code> tag:</p>
<div class="listingblock">
<div class="title">/grails-app/domain/demo/Player.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Player.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="type">class</span> <span class="class">Player</span> {
+ <span class="predefined-type">String</span> name
+ <span class="predefined-type">String</span> game
+ <span class="predefined-type">String</span> region
+ <span class="type">int</span> wins = <span class="integer">0</span>
+ <span class="type">int</span> losses = <span class="integer">0</span>
+
+ <span class="directive">static</span> constraints = {
+ name <span class="key">blank</span>: <span class="predefined-constant">false</span>, <span class="key">unique</span>: <span class="predefined-constant">true</span>
+ game <span class="key">blank</span>: <span class="predefined-constant">false</span>
+ region <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ wins <span class="key">min</span>: <span class="integer">0</span>
+ losses <span class="key">min</span>: <span class="integer">0</span>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -511,7 +543,7 @@ add simple error-checking. Near the top of <code>PlayerController.groovy</code>,
<div class="listingblock">
<div class="title">/grails-app/controllers/demo/PlayerController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/PlayerController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"></code></pre>
</div>
</div>
<div class="paragraph">
@@ -560,13 +592,46 @@ line to the dependencies block.</p>
<div class="listingblock">
<div class="title">/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy">dependencies {
+ testCompile <span class="string"><span class="delimiter">"</span><span class="content">org.grails:grails-datastore-rest-client</span><span class="delimiter">"</span></span>
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/demo/demo/PlayerControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/demo/PlayerControllerFuncSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+<span class="keyword">import</span> <span class="include">grails.plugins.rest.client.RestBuilder</span>
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+
+<span class="annotation">@SuppressWarnings</span>([<span class="string"><span class="delimiter">'</span><span class="content">LineLength</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">MethodName</span><span class="delimiter">'</span></span>])
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">PlayerControllerFuncSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">'</span><span class="content">test save validation</span><span class="delimiter">'</span></span>() {
+ <span class="key">given</span>:
+ RestBuilder rest = <span class="keyword">new</span> RestBuilder()
+
+ <span class="key">when</span>:
+ <span class="keyword">def</span> resp = rest.post(<span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">${</span>serverPort<span class="inline-delimiter">}</span></span><span class="content">/player/save</span><span class="delimiter">"</span></span>) { <i class="conum" data-value="1"></i><b>(1)</b>
+ accept(<span class="string"><span class="delimiter">'</span><span class="content">application/json</span><span class="delimiter">'</span></span>) <i class="conum" data-value="2"></i><b>(2)</b>
+ contentType(<span class="string"><span class="delimiter">'</span><span class="content">application/json</span><span class="delimiter">'</span></span>) <i class="conum" data-value="3"></i><b>(3)</b>
+ json {
+ name = <span class="string"><span class="delimiter">'</span><span class="content">Bob Smith</span><span class="delimiter">'</span></span>
+ wins = <span class="integer">42</span>
+ losses = <span class="string"><span class="delimiter">'</span><span class="content">abc</span><span class="delimiter">'</span></span>
+ }
+ }
+
+ <span class="key">then</span>:
+ resp.status == <span class="integer">422</span> <i class="conum" data-value="4"></i><b>(4)</b>
+ resp.json.errors.size() == <span class="integer">2</span>
+ resp.json.errors.find { <span class="local-variable">it</span>.field == <span class="string"><span class="delimiter">'</span><span class="content">losses</span><span class="delimiter">'</span></span> }.message == <span class="string"><span class="delimiter">'</span><span class="content">Property losses is type-mismatched</span><span class="delimiter">'</span></span>
+ resp.json.errors.find { <span class="local-variable">it</span>.field == <span class="string"><span class="delimiter">'</span><span class="content">game</span><span class="delimiter">'</span></span> }.message == <span class="string"><span class="delimiter">'</span><span class="content">Property [game] of class [class demo.Player] cannot be null</span><span class="delimiter">'</span></span>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -595,7 +660,24 @@ to actually save the object and respond to the form submission. This completes t
</div>
<div class="listingblock">
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/PlayerController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> <span class="function">save</span>(Player player) {
+ <span class="keyword">if</span> (player == <span class="predefined-constant">null</span>) {
+ render <span class="key">status</span>: HttpStatus.NOT_FOUND
+ <span class="keyword">return</span>
+ }
+
+ <span class="keyword">if</span> (player.hasErrors()) {
+ respond player.errors, <span class="key">view</span>: <span class="string"><span class="delimiter">'</span><span class="content">create</span><span class="delimiter">'</span></span>
+ <span class="keyword">return</span>
+ }
+
+ player.save <span class="key">flush</span>: <span class="predefined-constant">true</span>
+
+ request.withFormat {
+ form multipartForm { redirect player }
+ <span class="string"><span class="delimiter">'</span><span class="content">*</span><span class="delimiter">'</span></span> { respond player, <span class="key">status</span>: HttpStatus.CREATED }
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -710,7 +792,11 @@ form values will be bound to the command object’s properties where the nam
</div>
<div class="listingblock">
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/PlayerController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">PlayerInfo</span> {
+ <span class="predefined-type">String</span> name
+ <span class="predefined-type">String</span> game
+ <span class="predefined-type">String</span> region
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -770,7 +856,39 @@ submission.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/demo/PlayerControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/demo/PlayerControllerSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+<span class="keyword">import</span> <span class="include">grails.testing.gorm.DomainUnitTest</span>
+<span class="keyword">import</span> <span class="include">grails.testing.web.controllers.ControllerUnitTest</span>
+
+<span class="annotation">@SuppressWarnings</span>([<span class="string"><span class="delimiter">'</span><span class="content">MethodName</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">DuplicateListLiteral</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">DuplicateNumberLiteral</span><span class="delimiter">'</span></span>, <span class="string" [...]
+<span class="type">class</span> <span class="class">PlayerControllerSpec</span> <span class="directive">extends</span> Specification <span class="directive">implements</span> ControllerUnitTest<PlayerController>, DomainUnitTest<Player> {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test update</span><span class="delimiter">"</span></span>() {
+ <span class="key">when</span>:
+ <span class="keyword">def</span> player = <span class="keyword">new</span> Player(<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Sergio</span><span class="delimiter">'</span></span>, <span class="key">game</span>: <span class="string"><span class="delimiter">'</span><span class="content">XCOM: Enemy Unkown</span><span class="delimiter">'</span></span>, <span class="key">region</span>: <span class="string"><span class="de [...]
+ player.save()
+ params.id = player.id
+ params.name = <span class="string"><span class="delimiter">'</span><span class="content">Sergio del Amo</span><span class="delimiter">'</span></span>
+ params.game = <span class="string"><span class="delimiter">'</span><span class="content">XCOM 2</span><span class="delimiter">'</span></span>
+ params.region = <span class="string"><span class="delimiter">'</span><span class="content">USA</span><span class="delimiter">'</span></span>
+ params.wins = <span class="integer">4</span>
+ controller.update() <i class="conum" data-value="1"></i><b>(1)</b>
+
+ <span class="key">then</span>: <span class="string"><span class="delimiter">'</span><span class="content">respond model has no errors</span><span class="delimiter">'</span></span>
+ !model.player.hasErrors()
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">player properties have been updated correctly</span><span class="delimiter">'</span></span>
+ model.player.name == <span class="string"><span class="delimiter">'</span><span class="content">Sergio del Amo</span><span class="delimiter">'</span></span>
+ model.player.game == <span class="string"><span class="delimiter">'</span><span class="content">XCOM 2</span><span class="delimiter">'</span></span>
+ model.player.region == <span class="string"><span class="delimiter">'</span><span class="content">USA</span><span class="delimiter">'</span></span>
+
+ <span class="key">and</span>: <span class="string"><span class="delimiter">'</span><span class="content">non existing properties in the command object have not been modified</span><span class="delimiter">'</span></span>
+ model.player.wins == <span class="integer">3</span>
+ model.player.losses == <span class="integer">2</span>
+ }
+}</code></pre>
</div>
</div>
<div class="colist arabic">
@@ -796,7 +914,7 @@ and add constraints.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/demo/PlayerController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/PlayerController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"></code></pre>
</div>
</div>
<div class="paragraph">
@@ -805,7 +923,50 @@ and add constraints.</p>
<div class="listingblock">
<div class="title">/src/test/groovy/demo/PlayerInfoSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/test/groovy/demo/PlayerInfoSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+
+<span class="annotation">@SuppressWarnings</span>([<span class="string"><span class="delimiter">'</span><span class="content">MethodName</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">DuplicateListLiteral</span><span class="delimiter">'</span></span>])
+<span class="type">class</span> <span class="class">PlayerInfoSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test PlayerInfo.region can be null</span><span class="delimiter">"</span></span>() {
+ <span class="key">expect</span>:
+ <span class="keyword">new</span> PlayerInfo(<span class="key">region</span>: <span class="predefined-constant">null</span>).validate([<span class="string"><span class="delimiter">'</span><span class="content">region</span><span class="delimiter">'</span></span>])
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test PlayerInfo.name can be blank</span><span class="delimiter">"</span></span>() {
+ <span class="key">when</span>:
+ <span class="keyword">def</span> playerInfo = <span class="keyword">new</span> PlayerInfo(<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>)
+
+ <span class="key">then</span>:
+ !playerInfo.validate([<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>])
+ playerInfo.errors[<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>].code == <span class="string"><span class="delimiter">'</span><span class="content">blank</span><span class="delimiter">'</span></span>
+
+ <span class="key">when</span>:
+ playerInfo = <span class="keyword">new</span> PlayerInfo(<span class="key">name</span>: <span class="predefined-constant">null</span>)
+
+ <span class="key">then</span>:
+ !playerInfo.validate([<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>])
+ playerInfo.errors[<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>].code == <span class="string"><span class="delimiter">'</span><span class="content">nullable</span><span class="delimiter">'</span></span>
+ }
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">"</span><span class="content">test PlayerInfo.game can be blank</span><span class="delimiter">"</span></span>() {
+ <span class="key">when</span>:
+ <span class="keyword">def</span> playerInfo = <span class="keyword">new</span> PlayerInfo(<span class="key">game</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>)
+
+ <span class="key">then</span>:
+ !playerInfo.validate([<span class="string"><span class="delimiter">'</span><span class="content">game</span><span class="delimiter">'</span></span>])
+ playerInfo.errors[<span class="string"><span class="delimiter">'</span><span class="content">game</span><span class="delimiter">'</span></span>].code == <span class="string"><span class="delimiter">'</span><span class="content">blank</span><span class="delimiter">'</span></span>
+
+ <span class="key">when</span>:
+ playerInfo = <span class="keyword">new</span> PlayerInfo(<span class="key">game</span>: <span class="predefined-constant">null</span>)
+
+ <span class="key">then</span>:
+ !playerInfo.validate([<span class="string"><span class="delimiter">'</span><span class="content">game</span><span class="delimiter">'</span></span>])
+ playerInfo.errors[<span class="string"><span class="delimiter">'</span><span class="content">game</span><span class="delimiter">'</span></span>].code == <span class="string"><span class="delimiter">'</span><span class="content">nullable</span><span class="delimiter">'</span></span>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -814,7 +975,25 @@ and add constraints.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/demo/PlayerController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/PlayerController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> <span class="function">update</span>(PlayerInfo info) {
+ <span class="keyword">if</span> (info.hasErrors()) {
+ respond info.errors, <span class="key">view</span>: <span class="string"><span class="delimiter">'</span><span class="content">edit</span><span class="delimiter">'</span></span>
+ <span class="keyword">return</span>
+ }
+ Player player = Player.get(params.id)
+ <span class="keyword">if</span> (player == <span class="predefined-constant">null</span>) {
+ render <span class="key">status</span>: HttpStatus.NOT_FOUND
+ <span class="keyword">return</span>
+ }
+
+ player.properties = info.properties
+ player.save <span class="key">flush</span>: <span class="predefined-constant">true</span>
+
+ request.withFormat {
+ form multipartForm { redirect player }
+ <span class="string"><span class="delimiter">'</span><span class="content">*</span><span class="delimiter">'</span></span> { respond player, <span class="key">status</span>: HttpStatus.OK }
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
diff --git a/guides/command-objects-and-forms/3/guide/pages/addCreateAndSaveActions.html b/guides/command-objects-and-forms/3/guide/pages/addCreateAndSaveActions.html
index 40eecae13a9..b74631e495f 100644
--- a/guides/command-objects-and-forms/3/guide/pages/addCreateAndSaveActions.html
+++ b/guides/command-objects-and-forms/3/guide/pages/addCreateAndSaveActions.html
@@ -91,7 +91,10 @@ action.</p>
<div class="listingblock">
<div class="title">/grails-app/controllers/demo/PlayerController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/PlayerController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="annotation">@SuppressWarnings</span>([<span class="string"><span class="delimiter">'</span><span class="content">FactoryMethodName</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">GrailsMassAssignment</span><span class="delimiter">'</span></span>])
+<span class="keyword">def</span> <span class="function">create</span>() {
+ respond <span class="keyword">new</span> Player(params)
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -164,7 +167,23 @@ the <code><form></code> tag:</p>
<div class="listingblock">
<div class="title">/grails-app/domain/demo/Player.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/domain/demo/Player.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="type">class</span> <span class="class">Player</span> {
+ <span class="predefined-type">String</span> name
+ <span class="predefined-type">String</span> game
+ <span class="predefined-type">String</span> region
+ <span class="type">int</span> wins = <span class="integer">0</span>
+ <span class="type">int</span> losses = <span class="integer">0</span>
+
+ <span class="directive">static</span> constraints = {
+ name <span class="key">blank</span>: <span class="predefined-constant">false</span>, <span class="key">unique</span>: <span class="predefined-constant">true</span>
+ game <span class="key">blank</span>: <span class="predefined-constant">false</span>
+ region <span class="key">nullable</span>: <span class="predefined-constant">true</span>
+ wins <span class="key">min</span>: <span class="integer">0</span>
+ losses <span class="key">min</span>: <span class="integer">0</span>
+ }
+}</code></pre>
</div>
</div>
<div class="paragraph">
@@ -192,7 +211,7 @@ add simple error-checking. Near the top of <code>PlayerController.groovy</code>,
<div class="listingblock">
<div class="title">/grails-app/controllers/demo/PlayerController.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>grails-app/controllers/demo/PlayerController.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"></code></pre>
</div>
</div>
<div class="paragraph">
@@ -241,13 +260,46 @@ line to the dependencies block.</p>
<div class="listingblock">
<div class="title">/build.gradle</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>build.gradle[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy">dependencies {
+ testCompile <span class="string"><span class="delimiter">"</span><span class="content">org.grails:grails-datastore-rest-client</span><span class="delimiter">"</span></span>
+}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">/src/integration-test/groovy/demo/demo/PlayerControllerSpec.groovy</div>
<div class="content">
-<pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">link</span>:..<span class="regexp"><span class="delimiter">/</span><span class="content">snippets</span><span class="delimiter">/</span></span>src/integration-test/groovy/demo/PlayerControllerFuncSpec.groovy[role=include]</code></pre>
+<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> demo
+
+<span class="keyword">import</span> <span class="include">spock.lang.Specification</span>
+<span class="keyword">import</span> <span class="include">grails.plugins.rest.client.RestBuilder</span>
+<span class="keyword">import</span> <span class="include">grails.testing.mixin.integration.Integration</span>
+
+<span class="annotation">@SuppressWarnings</span>([<span class="string"><span class="delimiter">'</span><span class="content">LineLength</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">MethodName</span><span class="delimiter">'</span></span>])
+<span class="annotation">@Integration</span>
+<span class="type">class</span> <span class="class">PlayerControllerFuncSpec</span> <span class="directive">extends</span> Specification {
+
+ <span class="keyword">def</span> <span class="string"><span class="delimiter">'</span><span class="content">test save validation</span><span class="delimiter">'</span></span>() {
+ <span class="key">given</span>:
+ RestBuilder rest = <span class="keyword">new</span> RestBuilder()
+
+ <span class="key">when</span>:
+ <span class="keyword">def</span> resp = rest.post(<span class="string"><span class="delimiter">"</span><span class="content">http://localhost:</span><span class="inline"><span class="inline-delimiter">${</span>serverPort<span class="inline-delimiter">}</span></span><span class="content">/player/save</span><span class="delimiter">"</span></span>) { <i class="conum" data-value="1"></i><b>(1)</b>
+ accept(<span class="string"><span class="delimiter">'</span><span class="content">application/json</span><span class="delimiter">'</span></span>) <i class="conum" data-value="2"></i><b>(2)</b>
+ contentType(<span class="string"><span class="delimiter">'</span><span class="content">application/json</span><span class="delimiter">'</span></span>) <i class="conum" data-value="3"></i><b>(3)</b>
+ json {
+ name = <span class="string"><span class="delimiter">'</span><span class="content">Bob Smith</span><span class="delimiter">'</span></span>
+ wins = <span class="integer">42</span>
+ losses = <span class="string"><span class="delimiter">'</span><span class="content">abc</span><span class="delimiter">'</span></span>
+ }
+ }
... 136328 lines suppressed ...