(Quick Reference)

4.3 重用Grails脚本 - Reference Documentation

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith

Version: null

4.3 重用Grails脚本

Grails带了许多开箱即用的命令行功能,你会发现这在你自己的脚本中那个会很有用(查看参考指南的命令行指南部分可以获得所有命令的详细信息)。尤其是使用 compile, packagebootstrap 脚本。

下边的bootstrap脚本例子允许你启动一个Spring的 ApplicationContext 实例,通过它来访问数据源等(集成测试时可以这样用):

includeTargets << grailsScript("_GrailsBootstrap")

target ('default': "Database stuff") { depends(configureProxy, packageApp, classpath, loadApp, configureApp)

Connection c try { c = appCtx.getBean('dataSource').getConnection() // do something with connection } finally { c?.close() } }

从其他脚本文件引入任务

Gant允许你从另一个Gant脚本文件中引入所有任务(除了“default”)。然后你就可以depend或调用这些已经被定义在当前脚本文件中的任务了。实现的途径是includeTargets属性。使用左移操作符来简单的“附加”一个文件或类:
includeTargets << new File("/path/to/my/script.groovy")
includeTargets << gant.tools.Ivy
不用太担心关于使用一个类的语法,它是相当特殊的。要是你感兴趣,可以看看Gant的文档。

核心的Grails任务

如你在本章开头部分所看到的例子,当使用includeTargets来包含核心的Grails任务时,既没有使用基于文件的语法也没有使用基于类的语法。取而代之的,你应该使用Grails命令启动器提供的特殊的grailsScript()方法(注意这个方法在一般的Gant脚本中是不可用的,只有在Grails环境中才行)。

grailsScript() 方法的语法是非常简单易读的:简单的把你想要包含的Grails脚本文件的名称传入,不需要任何路径信息。以下是一个你可能想要重用的Grails脚本列表:

脚本描述
_GrailsSettings你确实应该包括这个!幸运的是,它已经被所有其他Grails脚本文件自动包括了(除了_GrailsProxy),因此你通常不必明确的包括它。
_GrailsEvents如果你想要触发事件,你应该包括这个。添加一个event(String eventName, List args)方法。另外,这也被几乎所有其他Grails脚本文件包括。
_GrailsClasspath安装编译、测试和运行用的classpath。如果你想使用它们,就包含这个脚本。另外,这也由几乎所有其他Grails脚本包含。
_GrailsProxy如果你不直接接入互联网,而是使用代理,包含这个脚本,配置代理参数。
_GrailsArgParsing提供一个parseArguments任务,就像字面上的意思:当运行你的脚本的时候解析用户提供的参数。把参数添加到argsMap属性中。
_GrailsTest包含所有共享的测试代码。如果你要添加额外的测试这将非常有用。
_GrailsRun为你提供在配置好的servlet容器中运行应用程序时需要的一切,可以是正常的运行(runApp/runAppHttps) ,也可以是来自于一个WAR文件(runWar/runWarHttps)。

这些由Grails提供的脚本很值得对它们进行深入的分析,从而找出哪些类型的任务是可以使用的。任何脚本文件都是以"_"作为前缀以便进行重用。

脚本结构

你可能对这些下划线词语作为Grails脚本的名称感到疑惑。用_internal_作为一个脚本或者用没有对应的“command”的其他单词,这些就是Grails的决定方式。因此无法运行例如"grails _grails-settings"这样的命令。这也就是为什么它们没有个默认的任务。

内部脚本是和代码共享重用相关的。实际上,我们建议在自己的脚本中使用类似的方式:把你的所有任务放入一个内部脚本中可以更容易的共享,然后提供简单的命令脚本来解析任何命令行参数并委托给内部脚本中的任务。假如你有一个脚本要运行一些功能测试——你可以将它们像这样分离:

./scripts/FunctionalTests.groovy:

includeTargets << new File("${basedir}/scripts/_FunctionalTests.groovy")

target(default: "Runs the functional tests for this project.") { depends(runFunctionalTests) }

./scripts/_FunctionalTests.groovy:

includeTargets << grailsScript("_GrailsTest")

target(runFunctionalTests: "Run functional tests.") { depends(...) … }

以下是在编写脚本时常用的一些指导方案:

  • 将脚本分为“command”脚本和内部脚本。
  • 将大部分执行脚本放入内部脚本。
  • 将参数解析放入“command”脚本。
  • 要把参数传入一个任务,先创建一些脚本变量并在调用任务前将它们初始化。
  • 为了避免名称冲突,可以为脚本变量分配闭包以替代任务。之后你可以直接将参数传入闭包。

Grails ships with a lot of command line functionality out of the box that you may find useful in your own scripts (See the command line reference in the reference guide for info on all the commands). Of particular use are the compile, package and bootstrap scripts.

The bootstrap script for example lets you bootstrap a Spring ApplicationContext instance to get access to the data source and so on (the integration tests use this):

includeTargets << grailsScript("_GrailsBootstrap")

target ('default': "Database stuff") { depends(configureProxy, packageApp, classpath, loadApp, configureApp)

Connection c try { c = appCtx.getBean('dataSource').getConnection() // do something with connection } finally { c?.close() } }

Pulling in targets from other scripts

Gant lets you pull in all targets (except "default") from another Gant script. You can then depend upon or invoke those targets as if they had been defined in the current script. The mechanism for doing this is the includeTargets property. Simply "append" a file or class to it using the left-shift operator:

includeTargets << new File("/path/to/my/script.groovy")
includeTargets << gant.tools.Ivy
Don't worry too much about the syntax using a class, it's quite specialised. If you're interested, look into the Gant documentation.

Core Grails targets

As you saw in the example at the beginning of this section, you use neither the File- nor the class-based syntax for includeTargets when including core Grails targets. Instead, you should use the special grailsScript() method that is provided by the Grails command launcher (note that this is not available in normal Gant scripts, just Grails ones).

The syntax for the grailsScript() method is pretty straightforward: simply pass it the name of the Grails script to include, without any path information. Here is a list of Grails scripts that you could reuse:

ScriptDescription
_GrailsSettingsYou really should include this! Fortunately, it is included automatically by all other Grails scripts except _GrailsProxy, so you usually don't have to include it explicitly.
_GrailsEventsInclude this to fire events. Adds an event(String eventName, List args) method. Again, included by almost all other Grails scripts.
_GrailsClasspathConfigures compilation, test, and runtime classpaths. If you want to use or play with them, include this script. Again, included by almost all other Grails scripts.
_GrailsProxyIf you don't have direct access to the internet and use a proxy, include this script to configure access through your proxy.
_GrailsArgParsingProvides a parseArguments target that does what it says on the tin: parses the arguments provided by the user when they run your script. Adds them to the argsMap property.
_GrailsTestContains all the shared test code. Useful if you want to add any extra tests.
_GrailsRunProvides all you need to run the application in the configured servlet container, either normally (runApp/runAppHttps) or from a WAR file (runWar/runWarHttps).

There are many more scripts provided by Grails, so it is worth digging into the scripts themselves to find out what kind of targets are available. Anything that starts with an "_" is designed for reuse.

Script architecture

You maybe wondering what those underscores are doing in the names of the Grails scripts. That is Grails' way of determining that a script is internal , or in other words that it has not corresponding "command". So you can't run "grails _grails-settings" for example. That is also why they don't have a default target.

Internal scripts are all about code sharing and reuse. In fact, we recommend you take a similar approach in your own scripts: put all your targets into an internal script that can be easily shared, and provide simple command scripts that parse any command line arguments and delegate to the targets in the internal script. For example if you have a script that runs some functional tests, you can split it like this:

./scripts/FunctionalTests.groovy:

includeTargets << new File("${basedir}/scripts/_FunctionalTests.groovy")

target(default: "Runs the functional tests for this project.") { depends(runFunctionalTests) }

./scripts/_FunctionalTests.groovy:

includeTargets << grailsScript("_GrailsTest")

target(runFunctionalTests: "Run functional tests.") { depends(...) … }

Here are a few general guidelines on writing scripts:

  • Split scripts into a "command" script and an internal one.
  • Put the bulk of the implementation in the internal script.
  • Put argument parsing into the "command" script.
  • To pass arguments to a target, create some script variables and initialise them before calling the target.
  • Avoid name clashes by using closures assigned to script variables instead of targets. You can then pass arguments direct to the closures.