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.
Related
First, I came from a .NET background so please excuse my lack of groovy lingo. Back when I was in a .NET shop, we were using TypeScript with C# to build web apps. In our controllers, we would always receive/respond with DTOs (data xfer objects). This got to be quite the headache every time you create/modify a DTO you had to update the TypeScript interface (the d.ts file) that corresponded to it.
So we created a little app (a simple exe) that loaded the dll from the webapp into it, then reflected over it to find the DTOs (filtering by specific namespaces), and parse through them to find each class name within, their properties, and their properties' data types, generate that information into a string, and finally saved as into a d.ts file.
This app was then configured to run on every build of the website. That way, when you go to run/debug/build the website, it would update your d.ts files automatically - which made working with TypeScript that much easier.
Long story short, how could I achieve this with a Grails Website if I were to write a simple groovy app to generate the d.ts that I want?
-- OR --
How do I get the IDE (ex IntelliJ) to run a groovy file (that is part of the app) that does this generation post-build?
I did find this but still need a way to run on compile:
Groovy property iteration
class Foo {
def feck = "fe"
def arse = "ar"
def drink = "dr"
}
class Foo2 {
def feck = "fe2"
def arse = "ar2"
def drink = "dr2"
}
def f = new Foo()
def f2 = new Foo2()
f2.properties.each { prop, val ->
if(prop in ["metaClass","class"]) return
if(f.hasProperty(prop)) f[prop] = val
}
assert f.feck == "fe2"
assert f.arse == "ar2"
assert f.drink == "dr2"
I've been able to extract the Domain Objects and their persistent fields via the following Gant script:
In scripts/Props.groovy:
import static groovy.json.JsonOutput.*
includeTargets << grailsScript("_GrailsBootstrap")
target(props: "Lists persistent properties for each domain class") {
depends(loadApp)
def propMap = [:].withDefault { [] }
grailsApp.domainClasses.each {
it?.persistentProperties?.each { prop ->
if (prop.hasProperty('name') && prop.name) {
propMap[it.clazz.name] << ["${prop.name}": "${prop.getType()?.name}"]
}
}
}
// do any necessary file I/O here (just printing it now as an example)
println prettyPrint(toJson(propMap))
}
setDefaultTarget(props)
This can be run via the command line like so:
grails props
Which produces output like the following:
{
"com.mycompany.User": [
{ "type": "java.lang.String" },
{ "username": "java.lang.String" },
{ "password": "java.lang.String" }
],
"com.mycompany.Person": [
{ "name": "java.lang.String" },
{ "alive": "java.lang.Boolean" }
]
}
A couple of drawbacks to this approach is that we don't get any transient properties and I'm not exactly sure how to hook this into the _Events.groovy eventCompileEnd event.
Thanks Kevin! Just wanted to mention, in order to get this to run, here are a few steps I had to make sure to do in my case that I thought I would share:
-> Open up the grails BuildConfig.groovy
-> Change tomcat from build to compile like this:
plugins {
compile ":tomcat:[version]"
}
-> Drop your Props.groovy into the scripts folder on the root (noting the path to the grails-app folder for reference)
[application root]/scripts/Props.groovy
[application root]/grails-app
-> Open Terminal
gvm use grails [version]
grails compile
grails Props
Note: I was using Grails 2.3.11 for the project I was running this on.
That gets everything in your script to run successfully for me. Now to modify the println portion to generate Typescript interfaces.
Will post a github link when it is ready so be sure to check back.
Grails 2.5.0
I'm defining an alternate context root via _Events.groovy using the eventConfigureTomcat closure. To do this, I've got code like so..
eventConfigureTomcat = { tomcat ->
def newContextRoot = "/"
def newContextPath = new File("/full/path/to/context/root")
// more code below that doesn't matter for this question...
}
For the newContextPath I want to pull that value from my Config.groovy (which is actually pulling it from an external config file). I've tried using grails.util.Holders but it doesn't seem to be wired up at this specific event yet. Is this possible?
You can set the value from your Config.groovy as an environment variable and then use it in _Events.groovy. If you still need to pull from an external config file, run a bash script inside of .bashrc/.bashprofile (wherever you define the environment variable).
eventConfigureTomcat = { tomcat ->
String altctxroot = System.getenv("ALT_CTX_ROOT")
def newContextRoot = "/"
def newContextPath = new File("$altctxroot")
}
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.
I have a singleton class
#Singleton
class CustomerBundleSingleton {
def grailsApplication = Holders.getGrailsApplication()
String projName
private CustomerBundleSingleton() {
line 10: projName = // how to get sub-project name here ???
}
}
application.properties // my project is running
-----------------------
app.name = MyNewProject
application.properties // located in sub project
-----------------------
app.name = MySubProject
I tried grailsApplication.metadata['app.name'] in "line 10:" it returns "MyNewProject".Whereas I want a way to get the project name of the UserBundleSingleton located (MySubProjectName). Something like grailsApplication.current.metadata['app.name'] ???? .
So that it can give me back MySubProjectName instead of MyNewProject??
I have 3 suggestions depending on your requirements and your 'bundling'.
1) You don't have a bundle marker/descriptor
Assuming that you know the sub-project(Grails plugin) name, your life gets easier, instead of having to loop through all plugins...
You can probably use something among these lines.
// Plugin name is 'hibernate' in this example
import org.codehaus.groovy.grails.plugins.PluginManagerHolder
def hibernateVersion = PluginManagerHolder.pluginManager.getGrailsPlugin('hibernate').version
// Loop through all plugins
// PluginManagerHolder.pluginManager.getAllPlugins()
2) Using custom plugin properties to lookup plugins of interest
Other strategy, if you must lookup the bundle dynamically.
Create a custom marker property in each of your plugin descriptors
def specialProperty = "whatever"
Then inside your CustomerBundleSingleton
PluginManagerHolder.pluginManager.getAllPlugins().each {
if (it.properties.specialProperty) {
def subProjectName = it.name
def subProjectVersion = it.version
}
}
3) Custom bundle info resolution
You may also want to consider some metadata via META-INF/MANIFEST.MF or similar mechanism.
Hope it helps...
The Email-ext of Jenkins allows you to write a Jelly email template. How do you write and test one without triggering a build every time? Basically, I'm looking for a 1 second iteration where I can modify a Jelly script, hit refresh on a browser, and it will automatically render the template based upon a hard-code project and build result.
Open Jenkins script console at _http://server/script/ (Stackoverflow is having issues saving an edit when this is an actual URL).
Enter the following code and replace your-project-name with the name of your project and me#me.com with your email address:
import hudson.model.StreamBuildListener
import hudson.plugins.emailext.ExtendedEmailPublisher
import java.io.ByteArrayOutputStream
def projectName = "your-project-name"
def project = Jenkins.instance.getItem(projectName)
try
{
def testing = Jenkins.instance.copy(project, "$projectName-Testing")
def build = project.lastUnsuccessfulBuild
// see the javadoc for the Job class for other ways to get builds
def baos = new ByteArrayOutputStream()
def listener = new StreamBuildListener(baos)
testing.publishersList.each() { p ->
println(p)
if(p instanceof ExtendedEmailPublisher) {
// modify the properties as necessary here
p.recipientList = 'me#me.com' // set the recipient list while testing
// run the publisher
p.perform((AbstractBuild<?,?>)build, null, listener)
// print out the build log from ExtendedEmailPublisher
println(new String( baos.toByteArray(), "UTF-8" ))
}
}
}
finally
{
if (testing != null)
{
testing.delete()
}
}
SOURCE: https://earl-of-code.com/2013/02/prototyping-and-testing-groovy-email-templates/
There is also an issue that tracks making this easier:
JENKINS-9594 - Should be able to send test e-mail based on previous build
There is now an option to test templates against builds in the more recent versions of the plugin. When you are on a job's screen, there should be a link on the left side that says Email Template Testing. It will let you select a build to test again and it will render the template right there.