Access Grails domain class from GroovyShell script - grails

I'm trying to use GroovyShell to evaluate some snippets of code from within a Grails application, but I don't seem to be able to access our Grails domain classes. For example when I try to evaluate something like this:
Eval.me("my.package.MyDomainClass.get(1)")
I see an error like this:
groovy.lang.MissingPropertyException: No such property: my for class: Script1
Any thoughts on how I can get this to work?
Thanks.

I figured out how to get this working. By default a GroovyShell instance clearly evaluates the script in its own classloader, so none of your Grails artefacts are available. There's an alternative constructor where you can pass in another classloader, so this does what I need within the context of a Grails application (when running inside a grails console, for example):
def shell = new GroovyShell(this.class.classLoader)
shell.evaluate("my.package.MyDomainClass.get(1)")

Related

Specify arguments in grails command

I'm using grails 3.2.6. I'm writing a custom grails command. It's a command and not a script because I need access to object in the spring application context in the command. I would like for my command to accept command line arguments with values such as "name" and "group" and "id". For example, I'd like to be able to run the command like this:
grails run-command process-group --name=foo --group=bar --id=2
However, when I do this, my command implementation class doesn't seem to have access to any of these arguments (name, group, or id). I've tried accessing them through the args attribute that comes from the GrailsApplicationCommand trait, but that is empty. I've also tried accessing it through various methods of the CommandLine object that is accessible through executionContext.commandLine object which also comes from the GrailsApplicationCommand trait. However, everything there seems to be empty as well (undeclaredOptions is empty, remainingArgs is empty, rawArguments just has my command name which is "process-group", etc.). If I remove the leading "--" from my argument names like this:
grails run-command process-group name=foo group=bar id=2
then the args attribute contains 3 entries: ["name=foo", "group=bar", and "id=2"]. I suppose that I could implement my command this way (no leading "--" on the command arguments), but it's kind of ugly, and it also means that I have to parse the arguments myself. The grails docs (http://docs.grails.org/latest/guide/single.html#creatingCustomCommands) say that, "Since Grails 3.2.0, commands have similar abilities as scripts in regards to retrieving arguments, template generation, file access, and model building." I'm interested in the retrieving arguments part of that statement. The examples shown in the documentation for scripts (not commands) show (http://docs.grails.org/latest/guide/single.html#creatingCustomScripts) how to declare parameters that a script will take from the command line. For example, this command line is supposed to work with a custom script that declares the "force" parameter for the generate-all custom script:
grails generate-all MyClass --force
This all seems to be tied to the use of the description() method in the script:
description( "Generates a controller that performs CRUD operations and the associated views" ) {
usage "grails generate-all <<DOMAIN CLASS>>"
flag name:'force', description:"Whether to overwrite existing files"
argument name:'Domain Class', description:'The name of the domain class'
}
However, there doesn't seem to be a description() method available for use for custom commands as there is for custom scripts. I just confused about how the grails docs state that retrieving arguments is supposed to now work the same for both custom scripts and custom commands, yet I can't seem to retrieve arguments from a custom command like I can from a custom script. Is there a way that I can retrieve the arguments for a custom command just like it can be done for a custom script?
Set the properties using the -D option:
-Dsample.message=foo
Grab the system properties using the follow code:
System.properties['sample.message']
Link to the where I found this example:
Passing Properties

Add logging to an external pluggable script

As described in Can't call one closure from another, I am using a pluggable script from within a Grails app.
Unfortunately, I've found that I can't use log4j from within these scripts. I am forced to use println.
I tried using
import org.apache.commons.logging.LogFactory
def Log log = LogFactory.getLog(getClass())
but I got no output. When I print out the result of the call to getClass(), I get something like
myscript$_run_closure5
So I'm thinking the issue is that there is no configuration in my Grails Config.groovy file for this class.
Is there a way for me to programmatically add these pluggable scripts to the log4j configuration? Keep in mind that I do not know in advance what the names of the scripts are, so this has to happen at runtime.
Consider the following code:
import org.apache.log4j.Logger
// ...
Logger log = Logger.getLogger('MyPlugin')
new File( grailsApplication.config.externalFiles ).eachFile { file ->
Binding binding = new Binding()
binding.variables.log = log
GroovyShell shell = new GroovyShell(binding)
shell.evaluate(file)
strategies.put( binding.variables.key, binding.variables )
}
Explanation:
It is not obligatory to pass class name to getLogger, it can be actually any string. You just need to make sure that this string is matched in log4j.properties of the main program.
You pass once created log to plugin scripts via binding variable "log". Then plugin scripts can access it simply as log.info('test123')
My personal recommendation would be to use logback instead of log4j. Both libraries were developed by the same guy and it is stated that logback supersedes log4j.

Unable to save a domain class instance from Groovy Console in GGTS

I am creating a Grails project using Groovy/Grails Tool Suite. If I choose Run As >> Groovy Console, I can create sample objects from my domain classes but cannot save them.
So,
new things.Thing(height: 20, length: 30)
creates a new Thing, but
new things.Thing(height: 20, length: 30).save()
throws an exception
No signature of method: thing.Thing.save() is applicable for argument types: () values: []
even though respondsTo states that Things respond to save().
Does anyone know why this is happening?
That's the Groovy console, not the Grails console. They use the same code and look the same, but the Grails version hooks into the application, whereas the Groovy console just has access to the code. To run the console, go to Grails Tools | Open Grails Command Prompt and enter console in the text field.
This is just a GUI wrapper for running grails console from the commandline.

How to know if a grails plugin is running standalone or when the plugin is installed into the application

Basically I want to retrieve the same value provided by the "pluginContextPath" variable used in a gsp but in a groovy class.
class {
String pluginCtx = $pluginContextPath
byte[] logo = grailsApplication.getParentContext().servletContext.getResource("${pluginCtx}images/myImage.jpg").bytes;
}
I know that I cannot use the $pluginContextPath variable in a groovy class, but I tried using something suggested in post but it gives me the dir full path
GrailsPluginUtils.pluginInfos.find { it.name == pluginName }.pluginDir
but what i want is what the pluginContextPath variable does
"When you run the plugin in development mode the link to the resource will resolve to something like /js/mycode.js. However, when the plugin is installed into an application the path will automatically change to something like /plugin/example-0.1/js/mycode.js and Grails will deal with making sure the resources are in the right place."

one-off grails script for populating database

Update: as of Grails 1.3.6 one has access to the full domain from Gant scripts.
From the Grails 1.3.6 release notes:
You can now run one or more Groovy scripts from the commandline using the run-script command, e.g.
grails run-script [path-to-script-1] [path-to-script-2]...[path-to-script-n]
This works around the issue in Gant scripts where you can't conveniently access application classes since they're not available in the classpath when the scripts start.
Hi all,
I am new to using Grails (in a real project) and I have a one-off script I need to execute that reads a file and then populates my database.
I wanted the script to run in the context of my grails app, so I used the create-script command. I now understand that makes it a 'Gant' script. The reason for doing so was that I thought it would allow me easy access to all the grails domain good-ness, so that i would be able to do something like this easily:
Car car = new Car(model: 'bar', brand: 'Ford')
car.save()
Here, Car is one of my domain classes and the strings 'bar' and 'Ford' I have retrieved from my file.
The start of my script looks like this:
import com.foo.Car
grailsHome = Ant.project.properties."environment.GRAILS_HOME"
includeTargets << new File ( "${grailsHome}/scripts/Bootstrap.groovy" )
target(main: "a script for storing cars") {
depends(bootstrap, classpath) // code dealing with the file with cars follows
Surprisingly, groovy gives me a java.lang.NoClassDefFoundError: com.foo.Car when I execute the script with the command grails LoadCars
Am I taking the wrong approach, or is there something more simple I am doing wrong?
Any help is appreciated
i know the scripts are useful, and I will probably get hate mail for even suggesting it, but I have just incorporating this kinda of stuff directly into my application in the past.
I have a flag set in my configuration which indicates if the data should be bootstrapped, if so, the bootstrap code looks for a comma delimited file at startup and calls a service method to load up the data.
I've updated the grails run-script Gant script (referred to by Jared above) to work with grails 1.3.5. I'd been meaning to do it for a while, but this question nudged me into finally getting around to it).
Just download the script described in the post, save it in your grails "scripts" directory and you can then run your own groovy script to bootstrap data with:
grails run-script script-path/boostrapMyDataIntoApp.groovy
I've had to do this and you have to create a special script to allow you to access GORM from a standard grails script. See this question for more info. I'm not sure what the current status of the script is under grails 1.3 but the author of the script posted in the comments.
Hans, there are several choices here, assuming you are not out to polish the GANT scripting chops 8^)
So assume that you are doing some integration-mode TDD, correct?
Have you looked into the db-stuff plugin? Actually that one leverages the open source package (extension of the JUnit project) called dbUnit, which is also an outstanding choice, for both Java and Groovy projects.
*db-stuff <0.3.0> -- db schema managment and data import/export. Generate generic schema files and import or export base/seed/test data into your database.
I have traditionally done this as well in the BootStrap depending on the environment - and I try to never let those domain assumptions / constraints get too far out of synch. with my schema.
Here's the canon I'm talking about :
class BootStrap {
def init = { servletContext ->
if (GrailsUtil.environment.equals( GrailsApplication.ENV_DEVELOPMENT )) {
log.info( "Loading sample data for 2010 models..." );
new Car( manufacturer: new Manufacturer( name: "Toyota" ), model: "Prius" )
new Car( manufacturer: new Manufacturer( name: "GM" ), model: "Volt" )
//...

Resources