Replace Grails Console with the Editor of Your Choice
2011/04/25The grails console has a number of disadvantages that make it a little clunky to use:
- You can’t attach it to a running grails instance, you need to run
grails consolefrom scratch - You can’t pass it parameters, such as the name of a script you’ve saved
- It’s a swing app, which is always a little wonky in it’s keybindings and it’s behavior
- It’s not <insert your chosen editor>
I got tired of these limitations so I decided to do something about it. I put together a groovy script that uses HTTPBuilder to POST groovy code to a running Grails app that has the grails console plugin installed.
If you’re running this in any non-development environment, you’ll want to ensure that it’s behind some form of authentication. The most popular grails security plugin is Burt Beckwith’s spring security core, so this script comes with built-in support for that.
This script makes it easy to call from any editor that allows you to execute scripts (pretty much every programmer’s editor on the market). At the bottom of the post, I show how to integrate it with Vim (my editor of choice), but it’d be just as easy to script it from TextMate, JEdit, IntelliJ, Eclipse, etc. Any editor that can take the file you’re currently editing and pass it to a shell script.
Here’s my Vim session with a grails script at the bottom and the output from that grails script in another window at the top.
This is the script that makes the magic happen (linked from github so any future tweaks will be reflected below), just grab a copy and save it somewhere in your path.
If you execute the script without any parameters, it’ll show you the usage syntax:
% postCode.groovy usage: postCode.groovy [-u username] [-p password] -b baseUrl file -b,--baseurl <baseUrl> The required base url to auth/post to, ex: http://localhost:8080/myapp/ -p,--password <password> The password to authenticate with -u,--user <user> The username to authenticate with, null username/password skips authentication
It uses the nifty CliBuilder that’s comes with recent versions of Groovy to parse command line arguments. If your project doesn’t have any authentication, you can simply call it with the -b argument to let it know where your app is running and pass it a script to execute on that running instance:
postCode.groovy -b http://localhost:8080/myapp/ scriptToRun.groovy ... results of scriptToRun.groovy ...
If your project IS using the spring security plugin for authentication, you can add a username and a password to use for authentication:
postCode.groovy -u admin -p sekrit -b http://localhost:8080/myapp/ scriptToRun.groovy ... results of scriptToRun.groovy ...
The first time that you run it, it might take a little while to run because the Grape Grab annotation is downloading the HTTPBuilder jars and it’s dependencies.
UPDATE: 8/7/2011 The below is no longer necessary with the new @GrabExclude to ignore the groovy dependency.
One sort of screwy thing that I’ve found with the default Grape settings is that it really isn’t that smart about what dependencies it should download again when they’re already satisfied. This can cause a ~30 second pause every time the script is run, which is unacceptable. I posted a message on the Groovy mailing list and the tl;dr fix for it is to increase the ivy.cache.ttl.default timeout in your ~/.groovy/grapeConfig.xml file (use this as a template if you don’t have the file).
<ivysettings> <property name="ivy.cache.ttl.default" value="15m"/> ...
Example Use with Spring Security Core Plugin
Here’s a quick demo project where the console plugin is protected by the Spring Security Core plugin.
grails create-app demo-post-code cd demo-post-code grails install-plugin console
Install the spring-security-core plugin and create the default User and Role domain classes.
grails install-plugin spring-security-core grails s2-quickstart com.example User Role
Edit grails-app/conf/Config.groovy and add a rule next to the other spring security configuration stating only ROLE_ADMIN users can get to the console plugin:
grails.plugins.springsecurity.controllerAnnotations.staticRules = [ '/console/**': ['ROLE_ADMIN'] ]
Now we’ll need an admin user in grails-app/conf/BootStrap.groovy that has permission to execute code in the console. Edit the bootstrap file to look like this:
import com.example.* class BootStrap { def springSecurityService def init = { servletContext -> def adminRole = Role.findByAuthority('ROLE_ADMIN') ?: new Role(authority: 'ROLE_ADMIN').save(failOnError: true) def adminUser = User.findByUsername('admin') ?: new User( username: 'admin', password: springSecurityService.encodePassword('sekrit'), enabled: true ).save(failOnError: true) if (!adminUser.authorities.contains(adminRole)) UserRole.create(adminUser, adminRole) } def destroy = { } }
As you can see, we’ve created an admin user with the password sekrit.
Now, create a sample script to run in the grails context, something like this:
import com.example.* println "User count = ${User.count()}"
Now run the app with grails run-app and in another terminal window execute the postCode.groovy script and give it the credentials and the base url to post it to. If you’ve got everything set up, you should see that there’s one User in the database (the admin user that we bootstrapped in :).
% postCode.groovy -u admin -p sekrit -b http://localhost:8080/demo-post-code test.groovy User count = 1
Integrating With Vim
My vimscript skills are pretty much non-existent, and I’m sure there’s a better way to do this, but here’s the code that I’ve got in my .vimrc to bind F7 to executing the current buffer as plain groovy, and binds F8 to executing it as a grails script, with full binding.
Shift-F7/F8 will close the execution window. If you highlight a part of your code in visual mode, it will just post the highlighted code rather than the whole file.
You can also override the default user/password/base url in a grails session by overriding any of these values, either directly in the vimscript or by setting the appropriate environment variable:
let g:grails_user = $DEFAULT_GRAILS_USER let g:grails_password = $DEFAULT_GRAILS_PASSWORD let g:grails_base_url = $DEFAULT_GRAILS_BASE_URL
So to test the above script, I’d use this in my .zshrc/.bashrc/.profile:
export DEFAULT_GRAILS_USER='admin' export DEFAULT_GRAILS_PASSWORD='sekrit' export DEFAULT_GRAILS_BASE_URL='http://localhost:8080/demo-post-code'
and this script to temporarily change the values using the current directory for the BASE_URL and optionally passing in a user and password
# example usage from root of grails directory: grailsBaseUrlReset myUsername myOtherPassword function grailsBaseUrlReset() { export DEFAULT_GRAILS_BASE_URL="http://localhost:8080/${PWD##*/}" echo "DEFAULT_GRAILS_BASE_URL set to $DEFAULT_GRAILS_BASE_URL" export DEFAULT_GRAILS_USER=$1 echo "DEFAULT_GRAILS_USER set to $DEFAULT_GRAILS_USER" export DEFAULT_GRAILS_PASSWORD=$2 echo "DEFAULT_GRAILS_PASSWORD set to $DEFAULT_GRAILS_PASSWORD" }
You can change them temporarily in vim by changing a value for your current grails session, just hit colon and change the value:
:let g:grails_password = "reallyReallySekrit"
(or you could add them to a different file that you source independently)
Here’s the full script, just add it to your ~/.vimrc and make sure the postCode.groovy script is in your path and you should be set to go:
function! s:copy_groovy_buffer_to_temp(first, last) " groovy/java scripts can't start with a # and tempname's normally do let src = substitute(tempname(), "[^\/]*$", "vim_&.groovy", "") " put current buffer's content in a temp file silent exe ": " . a:first . "," . a:last . "w " . src return src endfunction function! s:select_new_temp_buffer() let temp_file = tempname() " open the preview window to the temp file silent exe ":pedit! " . temp_file " select the temp buffer as active wincmd P " set options for temp buffer setlocal buftype=nofile setlocal noswapfile setlocal syntax=none setlocal bufhidden=delete return temp_file endfunction function! Groovy_eval_vsplit() range let temp_source = s:copy_groovy_buffer_to_temp(a:firstline, a:lastline) let temp_file = s:select_new_temp_buffer() " replace current buffer with groovy's output silent execute ":%! groovy " . temp_source . " 2>&1 " wincmd p " change back to the source buffer endfunction au BufNewFile,BufRead *.groovy vmap <silent> <F7> :call Groovy_eval_vsplit()<CR> au BufNewFile,BufRead *.groovy nmap <silent> <F7> mzggVG<F7>`z au BufNewFile,BufRead *.groovy imap <silent> <F7> <Esc><F7>a au BufNewFile,BufRead *.groovy map <silent> <S-F7> :wincmd P<CR>:q<CR> au BufNewFile,BufRead *.groovy imap <silent> <S-F7> <Esc><S-F7>a " set these up as environment variables on your system, or override " per session by using ':let g:grails_user = foo' let g:grails_user = $DEFAULT_GRAILS_USER let g:grails_password = $DEFAULT_GRAILS_PASSWORD let g:grails_base_url = $DEFAULT_GRAILS_BASE_URL function! Grails_eval_vsplit() range let temp_source = s:copy_groovy_buffer_to_temp(a:firstline, a:lastline) let temp_file = s:select_new_temp_buffer() if strlen(g:grails_user) > 0 " replace current buffer with grails' output silent execute ":%! postCode.groovy -u " . g:grails_user . " -p " . g:grails_password . " -b " . g:grails_base_url . " " . temp_source . " 2>&1 " else silent execute ":%! postCode.groovy -b " . g:grails_base_url . " " . temp_source . " 2>&1 " endif wincmd p " change back to the source buffer endfunction au BufNewFile,BufRead *.groovy vmap <silent> <F8> :call Grails_eval_vsplit()<CR> au BufNewFile,BufRead *.groovy nmap <silent> <F8> mzggVG<F8>`z au BufNewFile,BufRead *.groovy imap <silent> <F8> <Esc><F8>a au BufNewFile,BufRead *.groovy map <silent> <S-F8> :wincmd P<CR>:q<CR> au BufNewFile,BufRead *.groovy imap <silent> <S-F8> <Esc><S-F8>a
If you’re not a Vim user, it should be easy to integrate with any other editor that allows you to pass the current file to a shell command and display the results. I went a little extra with the vimscript to allow it to post the current buffer (or selected lines within that buffer) without having to save to disk first.

There are 9 comments in this article: