I was trying to follow the step-by-step instructions in Grails In Action (http://www.manning.com/gsmith2/GiA2E_meap_ch01.pdf), and the scaffolding explained in section 1.5.1, Pg. 21-23 does not seem to be working for me.
I added the static scaffold = true in the QuoteController.groovy as suggested. Then did grails run-app, and when I head to localhost:8080/qotd/quote/list I get a 404 error (instead of the Figure 1.11 in the pdf) as follows:
HTTP Status 404 - /qotd/quote/list
type Status report
message /qotd/quote/list
description The requested resource is not available.
Apache Tomcat/7.0.42
Here is the QuoteController.groovy:
package qotd
class QuoteController {
static scaffold = true
def index() {
redirect(action: "home")
}
def home() {
render "Real Programmers do not eat Quiche"
}
def random() {
def allQuotes = Quote.list()
def randomQuote
def n = allQuotes.size()
if (n > 0){
def randomIdx = new Random().nextInt(n)
randomQuote = allQuotes[randomIdx]
} else{
String str = "Real Programmers Don't Eat Quiche" + n
randomQuote = new Quote(author: "Anonymous",
content: str)
}
[quote: randomQuote]
}
}
However, going to localhost:8080/qotd/quote/create works fine (matches with Figure 1.12 in the pdf), and I am able to create a new quote.
The versions I am using are:
App version: 0.1
Grails version: 2.3.1
Groovy version: 2.1.8
JVM version: 1.7.0_45
Is this a bug in Grails or I am missing something?
I am new to Groovy and Grails, and any help would be highly appreciated.
Thank you!
The list action has been removed for some reason. Use index instead.
There are more changes now with version 2.4.2.
The following url explains how scaffolding has been moved to the plugin model:
http://grails.org/doc/latest/guide/scaffolding.html
"As of Grails 2.3, the scaffolding feature has been moved to a plugin. By default this is configured for installation in new applications, but if you are upgrading from a previous version of Grails you will need to add the following configuration to your BuildConfig.groovy file..."
So, inside the plugins { } section add this line:
compile ":scaffolding:2.0.0"
Also, use the action 'create' to force data into your database if it's still empty.
For example:
localhost:8080/myapp/mycont/create
Then try to see if you can load it with:
localhost:8080/myapp/mycont/show/1
Replace:
myapp --> with your application name (used in 'grails create-app')
mycont --> your controller name (used in 'grails create-controller')
Related
I use Spock to test an action of a controller as below:
code in controller:
def milestoneChange() {
Milestones selectedMilestone = dataQueryService.getMilestone(params.id)
Tasks[] tasksList = dataQueryService.getTasksList(params.id)
List enumerateNameList = dataQueryService.getEnumerateNameList()
render(template: 'milestoneSummary', model: [selectedMilestone: selectedMilestone, tasksList: tasksList, enumerateNameList: enumerateNameList, selectedMilestoneID: params.id])
}
code in test:
#TestFor(MilestonesMgtController)
class MilestonesMgtControllerSpec extends Specification {
void "change the milestone"() {
when:
views['/milestonesMgt/_milestoneSummary.gsp'] = "test"
controller.params.id = 'Late Stage Review'
controller.milestoneChange()
then:
controller.response.text == 'test'
}
}
Because the content of the _milestoneSummary.gsp is very long, I use the approach mentioned in both Grails documentation and http://www.javacodegeeks.com/2013/09/grails-goodness-unit-testing-render-templates-from-controller.html which is using views['/milestonesMgt/_milestoneSummary.gsp'] to mock the template. However, the response is still the original long content? Does anyone know what is wrong with my code? Thanks very much!!!
BTW when using params/response/model, I have to use controller.xxx, but I see the sample code in documentation use params/response/model directly, do you know why?
I am using Grails 2.3.8
the project link http://github.com/jackyying1130/MMS
the test is located in MMS/test/integration/phdmilestonemanagementsystem/MilestonesMgtControllerSpec.groovy
I'm running a test with Grails 2.3.8 and an external configuration, yet the value doesn't seem to be coming through. I've tried this in older versions and am not seeing the same error. Did something change or am I missing something that I fat fingered? I am using an absolute path and the file definitely exists because Grails sees the key.
Config.groovy
reliable.string = "This string is working"
grails.config.locations = ["file:/home/howes/Project/project/test-config.groovy"]
/home/howes/Project/project/test-config.groovy
test.externalstring = "this is a test"
To test it I made a controller and just called grailsApplication to pull out the values. The first time I load the page I get a blank map, and when I refresh I see the key but no value. To make sure I am pulling everything correctly I am outputting the test-config.groovy item and one from the basic Config.groovy.
TestContoller.groovy
class TestController {
def grailsApplication
def index() {
println grailsApplication.config.test
println grailsApplication.config.reliable
return [ext:grailsApplication.config.test.externalstring, ext2:grailsApplication.config.reliable.string]
}
}
Console Output (First Load)
[:]
[string:This string is working]
Console Output (Page Refresh)
[externalstring:[:]]
[string:This string is working]
What am I missing here?
I created a Grails plugin (grails create-plugin myplugin1) and noticed that there was no myapp1/grails-app/conf/BootStrap.groovy created like you normally get when you create a Grails app.
I created one like so:
class BootStrap {
def init = {
println("Hello! The plugin is bootstrapping...")
}
def destroy = {
}
}
I then include the plugin with a Grails app (by adding myplugin1 as a plugin inside the app's BuildConfig.groovy). When I issue a grails run-app, I don't see the above println ever executing.
Do Grails plugins not use BootStrap.groovy? If so, where should I put "bootstrap" code that needs to be executed when a plugin is loaded? Otherwise, if I was correct to do this above, why might I not be seeing the "Hello! The plugin is bootstrapping..." message print out?
As always, start with the very well written and maintained documentation.
Plugins do not include Bootstrap.groovy. The following are excluded from plugins (taken from documentation).
grails-app/conf/BootStrap.groovy
grails-app/conf/BuildConfig.groovy (although it is used to generate dependencies.groovy)
grails-app/conf/Config.groovy
grails-app/conf/DataSource.groovy (and any other *DataSource.groovy)
grails-app/conf/UrlMappings.groovy
grails-app/conf/spring/resources.groovy
Everything within /web-app/WEB-INF
Everything within /web-app/plugins/**
Everything within /test/**
SCM management files within /.svn/ and /CVS/
In order to run code on startup of your plugin you need to hook into the runtime configuration of your plugin by using the doWithSpring or doWithApplicationContext (depending on what you need to do).
All of this, and more, is explained in the documentation. An example might be:
// MyFancyPlugin.groovy
...
def doWithApplicationContext = { appCtx ->
def sessionFactory = appCtx.sessionFactory
// do something here with session factory
}
...
Plugin's BootStrap is EXCLUDED from the plugin package. You have to do your init-phase in the plugin descriptor, in one or several of the following closures:
def doWithSpring = {
def appName = application.metadata.'app.name'
}
def doWithDynamicMethods = { ctx ->
// TODO Implement registering dynamic methods to classes (optional)
}
def doWithApplicationContext = { applicationContext ->
// TODO Implement post initialization spring config (optional)
}
Code that needs to run at startup time should go in the doWithApplicationContext closure in the plugin descriptor (MyPlugin1GrailsPlugin.groovy).
Alternatively, call it something else (e.g. MyPluginBootStrap.groovy), as it's only the specific classes BootStrap and UrlMappings that are excluded when a plugin is packaged, but any class whose name ends BootStrap is considered a bootstrap artefact.
just started with the codenarc plugin for grails - seems to be great!
But now I try to configure a dynamic report title like this:
"code quality report for ${appName}"
Unfortunately, it seems that the appName property is not available at the right time - I just get a null value.
Any ideas?
PS: using Grails 2.0.4 with ":codenarc:0.19"
appName is by default bound in BuildConfig.groovy but is not available to the configuration closure for codenarc. I am yet to become cognizant of an absolute reasoning behind that behavior, unless anyone here throws some light on it. But you can very well read application.properties directly to get the appName as below:
codenarc.reports = {
def props = new Properties()
new File("application.properties").withInputStream {props.load(it)}
MyHtmlReport('html') {
outputFile = "${props['app.name']} - CodeNarc-Report.html"
title = "${props['app.name']} - Sample Report"
}
}
I have a pretty standard 2.0.3 Grails app and I've executed grails install-templates which places files list.gsp, edit.gsp, etc. in src/templates/scaffolding/ directory. These files are not automatically reloaded when a change is made to them. Is there a way I can get these to be automatically reloaded so I don't have to stop/start the app every time I make a change? I've tried looking at watchedResources but that seems to be plugin development related.
You are correct that the "watched-resources" mechanism only applies to plugins. The correct fix for this would be to modify the core ScaffoldingGrailsPlugin.groovy to add
def watchedResources = "file:./src/templates/scaffolding/*"
and it's probably worth submitting a JIRA to that effect. In the mean time, you may be able to get it working by writing a simple plugin of your own to "inject" this behaviour into the scaffolding plugin. Do grails create-plugin watch-scaffolding and then use the following for the plugin descriptor:
import org.codehaus.groovy.grails.plugins.GrailsPlugin
class WatchScaffoldingGrailsPlugin {
def version = "0.1"
def grailsVersion = "2.0 > *"
def dependsOn = [:]
def pluginExcludes = [ "grails-app/views/error.gsp" ]
def title = "Watch Scaffolding Plugin"
def author = "Your name"
def authorEmail = ""
def description = '''\
Watches for changes to scaffolding templates and reloads dynamically-scaffolded
controllers and views.
'''
// URL to the plugin's documentation
def documentation = "http://grails.org/plugin/watch-scaffolding"
// watch for changes to scaffolding templates...
def watchedResources = "file:./src/templates/scaffolding/*"
// ... and kick the scaffolding plugin when they change
def onChange = { event ->
event.manager.getGrailsPlugin('scaffolding').notifyOfEvent(
GrailsPlugin.EVENT_ON_CHANGE, null)
}
// rest of plugin options are no-op
def onConfigChange = { event -> }
def doWithWebDescriptor = { xml -> }
def doWithSpring = { }
def doWithDynamicMethods = { ctx -> }
def doWithApplicationContext = { applicationContext -> }
def onShutdown = { event -> }
}
Now in your application's BuildConfig.groovy add
grails.plugin.location.'watch-scaffolding' = '../watch-scaffolding'
(or whatever is the appropriate relative path from the root of your app to the root of the plugin) and your scaffolding template changes should start to reload automatically.
(This is tested on Grails 2.1, I initially tried using influences but it didn't have any effect, however forcing an onChange event in the scaffolding plugin had the required result.)
This code flushes scaffolding cache. You can create a specific admin action for it:
org.codehaus.groovy.grails.scaffolding.view.
ScaffoldingViewResolver.scaffoldedViews.clear()
According to GRAILS-755, this has been fixed, but I don't think it has because they don't reload for me either.
From that Jira, here is a possible workaround:
Use the console plugin, and run this command to clear the dynamic
scaffolded view cache:
def scaffoldedView =
org.codehaus.groovy.grails.scaffolding.view.ScaffoldingViewResolver.scaffoldedViews.clear()
After that, the next time I request a page, it doesn't find it in the
cache, and thus goes back to the disk to recreate it.