Gerald Bauer (Chairman, CEO, CFO and CTO of Me, Myself & I, Inc.)
Java User Group (JUG) Austria Talk, March 2004
Groovy is a dynamic object-oriented scripting language that combines the best from Smalltalk, Python and Ruby in an all-in-one package using a Java-like syntax. Groovy is 100 % Java and compiles scripts straight to Java bytecode that run on any Java Virtual Machine. Groovy offers seamless and smoth Java intergration: from Groovy you can access all Java libraries, you can build applets or Java beans, you can derive from Java classes in Groovy and vice versa.
Why yet another scripting language?
groovyc
)
as an alternative compiler to javac
Scripting on the Rise. The Death of General Purpose Languages and Monolithic Applications.
Prefer the single-purpose languages below to general-purpose languages such as Java, C# or Shark.
Groovy does not replace Java. Groovy complements Java and doesn't compete head-on. Groovy is a scripting language. Java is a hard-core programming language (systems language).
Java
public class ServusGroovy { public static void main( String args[] ) { System.out.println( "Servus Groovy" ); } } |
Groovy
print 'Servus Groovy' |
Java
import java.util.*; public class HelloWorld { public static void main( String args[] ) { List country = new ArrayList(); country.add( "Canada" ); country.add( "Austria" ); country.add( "Brazil" ); Collections.sort( country ); for( Iterator it = country.iterator(); it.hasNext() ) System.out.println( "Hello " + it.next() ); } } |
Groovy
country = [ 'Canada', 'Austria', 'Brazil' ] country.sort country.each { println "Hello ${it}" } |
Ruby
country = [ 'Canada', 'Austria', 'Brazil' ] country.sort country.each { |country| puts "Hello #{country}" } |
Java
public class Country { private String name; private String capital; public String getName() { return name; } public String getCapital() { return capital; } public String setName( String name ) { this.name = name; } public String setCapital( String capital ) { this.capital = capital; } public static void main( String args[] ) { Country austria = new Country(); austria.setName( "Austria" ); austria.setCapital( "Vienna" ); Country canada = new Country(); canada.setName( "Canada" ); canada.setCapital( "Ottawa" ); List world = new ArrayList(); world.add( austria ); world.add( canada ); for( Iterator it = world.iterator(); it.hasNext() ) { Country country = it.next(); System.out.println( "The capital of " + country.getName() + " is " + country.getCapital() + "." ); } } } |
Groovy
class Country { String name String capital } world = [new Country(name:'Austria', capital:'Vienna'), new Country(name:'Canada', capital:'Ottawa')] world.each { country | println "The capital of ${country.name} is ${country.capital}." } |
Ruby
class Country def initialize( name, capital ) @name = name @capital = capital end attr_reader( :name, :capital ) end world = [ Country.new( 'Austria', 'Vienna' ), Country.new( 'Canada', 'Ottawa' )] world.each { | country | puts "The capital of #{country.name} is #{country.capital}." } |
(1..1000)
) (=higher level data types)${movie.director.name}
)any
, each
, findAll
, print
and more)property
, using
)[1,2,3]+[3,4,5]
, map['one']
)Java
List list = new LinkedList(); list.add( new Integer( 1 ) ); list.add( new Integer( 2 ) ); list.add( new Integer( 3 ) ); |
Groovy
list = [1, 2, 3] |
Java
Map map = new HashMap(); map.put( "one", new Integer( 1 ) ); map.put( "two", new Integer( 2 ) ); map.put( "three", new Integer( 3 ) ); System.out.println( map.get( "one" ) ); |
Groovy
map = [ 'one' : 1, 'two' : 2, 'three': 3 ] print map[ 'one' ] |
Empty List
list = [] |
Empty Map
map = [:] |
Nested Lists
list = [1, 2, [4, 5], ‘hello’] |
Negative (Reverse) Index
last = list[-1] |
Ranges
list = 1..100 sub = list[1..20] sub = list[-5..-1] sub = list[1,5..10,15] |
Operator Overloading
[1,2,3] + [4,5,6] // returns [1,2,3,4,5,6] [1,2,3,4] - [2,4,6] // returns [1,3] map[ 'one' ] |
Many Helper Methods
[1,2,3,1,2,3].count(3) // return 2 ['one', 'two', 'three'].join( '+' ) [3,2,1].sort |
Java
for( int i=1; i<=1000; i++ ) System.out.println( "The lucky number is" + i + "today" ); |
Groovy
each
(1..1000).each { i | println "The lucky number is ${i} today" } |
or
(1..1000).each { println "The lucky number is ${it} today" } |
upto
1.upto(1000) { println "The lucky number is ${it} today" } |
step
1.step(1001,1) { println "The lucky number is ${it} today" } |
times
1000.times { println "Groovy rocks big time!" } |
Closures are a powerful way of passing around blocks of executable code (code blocks). Think of a closure as an anonymous inner method that can accept parameters.
{ x | println x } // parameter x { println it } // default parameter it { x, y | x > y } // parameter x, y { println "Hello" } // no parameters squared_closure = { x | x * x } // store closure in a variable squared_closure.call( 2 ) // call closure like a method; returns 4 |
Note, however that closures are more than anonymous inner methods.
Groovy supports true closures where state can be passed into and out of closures.
Any local variables in scope when the closure is created can be used and modified and
any variables created within the closure are visible outside as local variables.
No final
keyword is required like with Java and annonymous inner classes. Example:
count = 0 [1, 2, 3, 4].each { count += it; last = it } println( "the sum is ${count} and the last item was ${last}" ) |
Note that count
goes from the outer scope to the inner scope and
back out again. The last
variable goes from inside scope out.
Tip: A great way to learn more about closures is picking up a Ruby book such as the free Programming Ruby book by the Pragmatic Programmers. (Sorry there are no Groovy books yet on the market.)
Java (Using Standard Collection Library):
public List collectCounty( List data, String country ) { List result = new ArrayList(); for(Iterator i = data.iterator(); i.hasNext();) { Address address = (Address) i.next(); if( country.equals( address.getCountry()) ) result.add( address ); } return result; } List result = collectCountry( data, "Canada" ); |
Java (Using Groovy-ish Collection Library Mimicking Closures Using Anonymous Inner Single Method Interfaces):
Collection result = data.collect( new BooleanFilter() { public void isMatch( Object candidate ) { return( (Address)candidate ).getCountry().equals( "Canada" ); } }); |
Groovy (The Real Thing):
result = data.collect { address | "Canada" == address.country } |
Ruby:
result = data.collect { | address | "Canada" == address.country } |
each
[5, 9, 1, 6].each { println it } |
find
[5, 9, 1, 6].find { x | x > 5 } // returns 9 |
findAll
[5, 9, 1, 6].findAll { x | x > 5 } // returns [9, 6] |
map
[5, 9, 1, 6].map { x | x * 2 } // returns [10, 18, 2, 12] |
min / max
[5, 9, 1, 6].min() // returns 1 [5, 9, 1, 6].max() // returns 9 |
reverse
[1,2,3].reverse() // returns [3,2,1] |
Groovy adds new methods to the core Java classes to help productivity and polymorphism e.g. new closure methods: each, select, filter, collect
int | count(java.lang.Object value) |
void | each(groovy.lang.Closure closure) |
java.lang.Object | find(groovy.lang.Closure closure) |
java.util.List | findAll(groovy.lang.Closure closure) |
java.util.List | getAt(java.lang.String property) |
java.lang.String | join(java.lang.String separator) |
java.util.List | map(groovy.lang.Closure closure) |
java.lang.Object | max(java.util.Collection self) |
java.lang.Object | max(java.util.Comparator comparator) |
java.lang.Object | max(groovy.lang.Closure closure) |
java.lang.Object | min(java.util.Collection self) |
java.lang.Object | min(java.util.Comparator comparator) |
java.lang.Object | min(groovy.lang.Closure closure) |
java.util.List | plus(java.util.Collection right) |
java.util.List | plus(java.lang.Object right) |
and many more
void | eachByte(groovy.lang.Closure closure) |
void | eachFile(groovy.lang.Closure closure) |
void | eachLine(groovy.lang.Closure closure) |
void | splitEachLine(java.lang.String sep, groovy.lang.Closure closure) |
void | withOutputStream(groovy.lang.Closure closure) |
void | withPrintWriter(groovy.lang.Closure closure) |
void | withReader(groovy.lang.Closure closure) |
void | withWriter(groovy.lang.Closure closure) |
and many more
java.lang.Number | minus(java.lang.Number right) |
java.lang.Number | multiply(java.lang.Number right) |
java.lang.Number | plus(java.lang.Number right) |
java.lang.Number | power(java.lang.Number exponent) |
void | step(java.lang.Number to, java.lang.Number stepNumber, groovy.lang.Closure closure) |
void | times(groovy.lang.Closure closure) |
void | upto(java.lang.Number to, groovy.lang.Closure closure) |
and many more
boolean | any(groovy.lang.Closure closure) |
void | each(groovy.lang.Closure closure) |
boolean | every(groovy.lang.Closure closure) |
java.lang.Object | find(groovy.lang.Closure closure) |
java.lang.Object | findAll(groovy.lang.Closure closure) |
java.lang.Object | invokeMethod(java.lang.String method, java.lang.Object arguments) |
java.util.List | map(groovy.lang.Closure closure) |
void | print(java.lang.Object value) |
and many more
You can put Groovy expressions (including method calls)
inside strings
using the ${expression}
syntax similar to
Velocity or JSTL-EL.
Groovy
movie = "Lost in Translation" director = "Sofia Coppola" println "${director} directed the movie ${movie}." |
Java Flashback
String movie = "Lost in Translation"; String director = "Sofia Coppola"; System.out.println( director + " directed the movie " + movie + "." ); |
Groovy
num = 4 println "${num} squared equals ${num*num}" |
class Movie { String title Person director } class Person { String name } person = new Person( name:'Sofia Coppola' ) movie = new Movie( title:'Lost in Translation', director:person ) println "${movie.director.name} directed the movie ${movie.title}" // ${movie.director.name} is the same as movie.getDirector().getName() in Java |
In Groovy you can start and end strings with single or double quotes and use the other kind of quote without escaping inside the string. Example:
println "Alice says, 'Groovy rocks.'" println 'Alice says, "Groovy rocks."' |
In Groovy strings can span multiple lines. Example:
blurb = "Groovy is a dynamic object-oriented language that combines the best from Smalltalk, Python and Ruby in an all-in-one package using a Java-like syntax." println blurb |
You can create multi-line strings without quotes using what is called a "here-doc".
After the three less-than characters (e.g. <<<
)
use your own delimiter (e.g. EOS
)
to enclose your text.
out = <<<EOS <html> <head> <title>Groovy Servlet</title> </head> <body> Hello, ${request.remoteHost}: ${session.counter}! ${new Date()} </body> </html> EOS |
To avoid the risk of NullPointerException
when walking object hierachies
you can use the "->
"operator
instead of ".
".
class Movie { String title; Person director } class Person { String name } movie = new Movie( title:'Leaving Las Vegas' ) // Doesn't throw NullPointerException println "${movie->director->name} directed the movie ${movie.title}" // Throws NullPointerException println "${movie.director.name} directed the movie ${movie.title}" |
Not for Groovy strings only. You can use the Groovy path expression language (including closures) everywhere. Example:
if( customers.orders.any { it.amount > 1000 && it.product.type == "citrus" } ) { doSomething() } |
or
for( order in customers.findAll { it.country.code == "AT" }.orders ) { println "order ${order.id} has value ${order.value}" } |
Groovy supports regular expressions
with built-in syntax using the ~"..."
expression.
Plus Groovy supports the =~
(create Matcher) and ==~
(matches regex) operators.
Example:
Java
Pattern pattern = Pattern.compile( "java" ); |
Groovy
pattern =~ "java" |
Java
Matcher matcher = Pattern.compile( "java" ).matcher( "javajava" ); String answer = matcher.replaceAll( "kaffe" ); |
Groovy
matcher = "javajava" =~ "java" answer = matcher.replaceAll( "kaffe" ) |
Java
String answer = Pattern.compile( "java" ).matcher( "javajava" ).replaceFirst( "groovy" ); |
Groovy
answer = ("javajava" =~ "java").replaceFirst("groovy") |
Alternative XML syntax similar to Groovy map and list syntax but for trees of anything. Example:
import groovy.xml.MarkupBuilder; xml = new MarkupBuilder() xml.xul() { menubar( id:'MAIN' ) { menu( label:'Bookmarks' ) { menuitem( label:'Luxor XUL Project Page', link:'http://luxor-xul.sourceforge.net' ) menuitem( label:'Luxor XUL Tag Reference', link:'http://luxor-xul.sourceforge.net/luxorref.html' ) menuitem( label:'Petra Plugin Central', link:'http://petra.sourceforge.net' ) menuitem( label:'XUL Alliance', link:'http://xul.sourceforge.net' ) menuitem( label:'The Richmond Post', link:'http://xul.sourceforge.net/post' ) } } } println xml |
generates the following XML markup:
<xul> <menubar id="MAIN"> <menu label="Bookmarks"> <menuitem label="Luxor XUL Project Page" link="http://luxor-xul.sourceforge.net" /> <menuitem label="Luxor XUL Tag Reference" link="http://luxor-xul.sourceforge.net/luxorref.html" /> <menuitem label="Petra Plugin Central" link="http://petra.sourceforge.net" /> <menuitem label="XUL Alliance" link="http://xul.sourceforge.net" /> <menuitem label="The Richmond Post" link="http://xul.sourceforge.net/post" /> </menu> </menubar> </xul> |
Note that you can mix and match Groovy markup with Groovy script (e.g. loops, method calls, variables, expressions, conditionals and so on).
Using the built-in AntBuilder
you can use
Ant tasks and mix and match markup with
scripts (e.g. you can pass on variables to Ant tasks and use Groovy
code anywhere within the markup).
ant = new AntBuilder() // lets just call one task ant.echo("hello") // here's an example of a block of Ant inside GroovyMarkup ant.sequential { echo("inside sequential") myDir = "target/AntTest/" mkdir(dir:myDir) copy(todir:myDir) { fileset(dir:"src/test") { include(name:"**/*.groovy") } } echo("done") } // now lets do some normal Groovy again file = new File("target/AntTest/groovy/util/AntTest.groovy") assert file.exists() |
Plain Old Java snippet:
frame = new JFrame( "Counter" ); frame.getContentPane().setLayout( new FlowLayout() ); frame.setSize( 200, 100 ); display = new JTextField(); display.setPreferredSize( new Dimension( 200, 300 ); display.setHorizontalAlignment( SwingConstants.CENTER ); frame.getContentPane().add( display ); increment = new JButton( "Inc" ); increment.setSize( 65, 70 ); increment.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent ev ) { onIncrement(); } } ); frame.getContentPane().add( increment ); clear = new JButton( "Clear" ); clear.setSize( 65, 70 ); clear.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent ev ) { onClear(); } } ); frame.getContentPane().add( clear ); decrement = new JButton( "Dec" ); decrement.setSize( 65, 70 ); decrement.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent ev ) { onDecrement(); } } ); frame.getContentPane().add( decrement ); |
Groovy snippet:
swing = new SwingBuilder() frame = swing.frame( title:'Counter', size:[200,100]) { panel(layout:new FlowLayout() ) { display = textField( preferredSize:[200,30], horizontalAlignment:SwingConstants.CENTER ) button( text:"Inc", size:[65,70], actionPerformed:{ value++; setDisplay() } ) button( text:"Clear", size:[65,70], actionPerformed:{ value=0; setDisplay() } ) button( text:"Dec", size:[65,70], actionPerformed:{ value--; setDisplay() } ) } } |
Tip: Use XML UI Language (XML) for your Swing UIs. If you use XUL you can reuse your Swing UIs no matter what language you use (e.g. Java, Jython (Python for Java), Groovy, etc.). For an example see the Luxor XUL Java Example Suite (Ramses), the Luxor XUL Python Example Suite, and the Luxor XUL Groovy Example Suite all reusing the same XUL file below.
<xul> <vbox id="COUNTER"> <groupbox> <caption label="Counter"/> <textbox id="DISPLAY" style="align: center; color: yellow; background: black; font: 24 bold monospace;" /> </groupbox> <hbox> <button label="Dec (-)" command="dec" style="width: 90px" /> <button label="Clear" command="clear" style="width: 90px" /> <button label="Inc (+)" command="inc" style="width: 90px" /> </hbox> </vbox> </xul> |
Groovy snippet:
swt = new JFaceBuilder() shell = swt.shell() { mainapp = applicationWindow() { menuManager( text:"File" ) { action ( text:"Very Nice", closure:{ println "Very Nice !!!" } ) separator() action ( text:"Check me", checked:true, closure:{ println "I've been checked" } ) } menuManager( text:"Edit" ) { action ( text:"Say Hi Statusbar", closure:{ mainapp.setStatus('Hello ...') } ) } fillLayout ( type:"vertical" ) label( text:"A big red label", background:[204, 0, 0] ) label( text:"I can barelly read this", foreground:[0,200,0] ) label( text:"It sure looks like the dutch flag", foreground:[0,0,150], background:[0, 0, 153] ) } } |
Output
<xul> <menubar id="MAIN"> <menu label="Bookmarks"> <menuitem label="Luxor XUL Project Page" link="http://luxor-xul.sourceforge.net" /> <menuitem label="Luxor XUL Tag Reference" link="http://luxor-xul.sourceforge.net/luxorref.html" /> <menuitem label="The Memphis Sun" link="http://luxor-xul.sourceforge.net/sun" /> <menuitem label="Petra Plugin Central" link="http://petra.sourceforge.net" /> <menuitem label="XUL Alliance" link="http://xul.sourceforge.net" /> <menuitem label="The Richmond Post" link="http://xul.sourceforge.net/post" /> </menu> </menubar> </xul> |
Groovy Script using Groovy SQL and Groovy Markup
import groovy.xml.MarkupBuilder; import groovy.sql.Sql import java.sql.DriverManager Class.forName( "org.hsqldb.jdbcDriver" ) con = DriverManager.getConnection( "jdbc:hsqldb:.", "sa", "" ) sql = new Sql( con ) xml = new MarkupBuilder() xml.xul() { menubar( id:'MAIN' ) { menu( label:'Bookmarks' ) sql.queryEach( 'select title, link from bookmark' ) { row | menuitem( label:"${row.title}", link:"${row.link}" ) } } } println xml |
Note, that Groovy also supports static typing (that is, declaring the type of variables before usage) in case you want to publish a method or interface for use back in Java land.
As long as you use Groovy just for 'scripts' you can keep your code completely dynamically typed and work like in Ruby or Python.
You can evaluate any Groovy expression or script using
GroovyShell (that is, the groovy.lang.GroovyShell
class.)
To pass variables in and out use the groovy.lang.Binding
object. Example:
// call groovy expressions from Java code Binding binding = new Binding(); binding.setVariable( "foo", new Integer(2) ); GroovyShell shell = new GroovyShell( binding ); Object value = shell.evaluate( "println 'Hello Groovy!'; x = 123; return foo * 10" ); assert value.equals(new Integer(20)); assert binding.getVariable("x").equals( new Integer(123) ); |
You can load Groovy classes dynamically
into a Java program and execute them on the fly
using the GroovyClassLoader
. Example:
GroovyClassLoader loader = new GroovyClassLoader(); Class groovyClass = loader.parseClass( new File("src/test/groovy/script/HelloWorld.groovy") ); // lets call some method on an instance GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance(); Object[] args = {}; groovyObject.invokeMethod( "run", args ); |
groovyc
groovyc
lets you compile Groovy scripts ahead-of-time to Java bytecode (that is, .class
files).
Note, that every Groovy class becomes a "normal" Java class that you can use
inside your Java code.
The only difference between a Groovy generated Java class
and a plain old Java class is that
a Groovy generated Java class
supports the groovy.lang.GroovyObject
interface.
Usage Example
Compile all Groovy scripts in current folder to Java bytecode
groovyc *.groovy |
Compile the Groovy script Graph.groovy
to Java bytecode (e.g. Graph.class
)
groovyc Graph.groovy |
Compiler Options
Option | Description |
---|---|
--classpath path
|
Specify where to find user class files |
-d directory
| Specify where to place generated class files |
--strict
| Turn on strict type safety |
--version
| Print the version |
--help
| Print a synopsis of standard options |
Runtime Dependencies
Compiled Groovy scripts require two runtime libraries,
that is, the Groovy Runtime Library
and the ObjectWeb ASM Bytecode Library
e.g. groovy-1.0-beta-3.jar
(~400k)
and asm-1.4.1.jar
(~30k)
You can also compile Groovy scripts to Java bytecode using the <groovyc>
Ant task.
Ant Task Paramter
Attribute | Description | Required |
---|---|---|
sourcedir
|
Specify where to find Groovy script files | Yes |
destdir
|
Specify where to place generated class files | Yes |
classpath
|
Specify where to find user class files | No |
Example
<taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="test.classpath" /> <groovyc destdir="${basedir}/target/test-classes" srcdir="${basedir}/target/test-classes" listfiles="true"> <classpath refid="test.classpath" /> </groovyc> |
Jython (Python for Java)
http://www.jython.org |
http://sourceforge.net/projects/jython
Jython is a 100 % Java version of the Python scripting language that allows you to compile Python scripts to Java byte code that runs on any Java Virtual Machine. Jython offers seamless and smoth Java intergration: from Python you can access all Java libraries, you can build applets or Java beans, you can derive from Java classes in Python and vice versa. Like Python, and unlike Java, you can use Jython interactively: just type in some Jython code at the prompt and see the story unfold immediately; licensed under an Apache-style license; headed by Finn Bock
Java Lite/Dynamic Java - BeanShell (bsh)
http://www.beanshell.org |
http://sourceforge.net/projects/beanshell
BeanShell (bsh) is a free, open-source, small (~175k) scripting interpreter in 100 % Java that runs standard Java statements and expressions and also sports a dynamically typed, casting optional Java lite syntax; licensed under GNU Library GPL (LGPL); headed by Patrick Niemeyer.
Tcl - Jacl (Java Command Language)
http://tcljava.sourceforge.net |
http://sourceforge.net/projects/tcljava |
http://www.tcl.tk/software/java
Jacl is a free, open-source Tcl 8.x scripting interpreter in 100 % Java initially created by Sun Labs in 1998 and now headed Mo DeJong; licensed under a BSD license; Jon Ousterhout created Tcl in the late 80's as an embeddable command language for interactive Unix tools.
JavacScript - Rhino
http://www.mozilla.org/rhino
Rhino is a free, open-source Mozilla-licensed Javascript engine in 100 % Java. Netscape started work on Rhino in Fall 1997 for the now defunct "Javagator".
Ruby - JRuby
http://jruby.sourceforge.net |
http://sourceforge.net/projects/jruby
JRuby is a free, open-source Ruby interpreter in 100 % Java that you can easily add to your app to script any Java class.
Smalltalk - Bistro
http://bistro.sourceforge.net |
http://sourceforge.net/projects/bistro
Bistro is a free, open-source variant of Smalltalk in 100 % Java than runs on any Java Virtual Machine. Bistro mixes the best of Smalltalk and Java.
Lisp/Scheme - Kawa
http://www.gnu.org/software/kawa
Scheme interpreter in 100 % Java; licensed under the GNU GPL; headed by Per Bothner; (Scheme is a modern variant of Lisp.)
And Many More (Lua, Basic, and so on and on). See Robert Tolksdorf's list of programming languages for the Java Virtual Machine for more.
Feature | Groovy | Python | Tcl | Perl | Java Script | Ruby | Visual Basic | |
---|---|---|---|---|---|---|---|---|
Speed of use | Rapid development | |||||||
Flexible, rapid evolution | ||||||||
Great regular expressions | ||||||||
Breadth of functionality | Easily extensible | |||||||
Embeddable | ||||||||
Easy UIs | ||||||||
Internet and Web-enabled | ||||||||
Enterprise usage | Cross platform | |||||||
Internationalization support | ||||||||
Thread safe | ||||||||
Database access |
Source: http://www.tcl.tk/advocacy
Official Groovy Site
http://groovy.codehaus.org
Groovy Library Reference (Groovy JDK API)
http://groovy.codehaus.org/groovy-jdk.html
Groovy Wiki Wiki
http://wiki.codehaus.org/groovy
Groovy - Making Java More Funky
James Strachan @ Codehaus One Summit, August 2003, Amsterdam
http://codehaus.org/~jstrachan/Groovy/html/groovy.html
Groovy - Scripting for Java
Mark Volkmann (Object Computing Inc.), Java News Brief (JNB), February 2004
http://www.ociweb.com/jnb/jnbFeb2004.html
groovy-user Mailinglist
http://lists.codehaus.org/mailman/listinfo/groovy-user
| http://news.gmane.org/gmane.comp.lang.groovy.user
James Strachan's Blog (Groovy Creator)
http://radio.weblogs.com/0112098
Official Ruby Site
http://www.ruby-lang.org
Ruby - The Programmer's Best Friend
"One-Click" Ruby Installer for Windows
http://rubyinstaller.sourceforge.net
Self-contained "one-click" installer that comprises the Ruby language itself,
dozens of popular extensions and packages, a syntax-highlighting editor and execution environment,
and a Windows help file that contains the full text of the book, "Programming Ruby: The Pragmatic Programmer's Guide".
Programming Ruby: A Pragmatic Programmer's Guide by David Thomas and Andrew Hunt, 608 pages, Addison-Wesley, ISBN: 0201710897, (December 2000); Free Online Version |
and many more
Please see my Java User Group (JUG) Austria talk slides titled "Python and Jelly: Scripting Power for Java and XML: Do More With Less (Lines of Code)" online @ http://luxor-xul.sourceforge.net/talk/jug-feb-2003/slides.html
Paper by John K. Ousterhout, Father of Tcl/Tk, published in IEEE Computer in March 1998
Scripting Is On The Rise; Different Tools for Different Tasks
Scripting languages and system programming languages are complementary, and most major computing platforms since the 60's have provided both kinds of languages. The languages are typically used together in component frameworks, where components are created with system programming languages and glued together with scripting languages. However, serveral recent trends, such as faster machines, better scripting languages, the increasing importance of graphical user interfaces and component architectures, and the growth of the Internet, have greatly increased the applicability of scripting languages.
Full story at http://home.pacbell.net/ouster/scripting.html