On Github cfaddict / cfo-groovy
Groovy Console
Command Line Tools
http://groovy.codehaus.org/api Cover GVM only enough to say there is a tool out there and provide a link at the end.def x = 11 x = [ foo : 'bar' ]
Groovy Strings (g-strings) are really powerful
Think ## but cooler, because it's Groovy
Whole lot of code examples here. Be sure to include 'lazy loading' examples G strings - yes they are reall called that You use intorpolation by putting a dollar in front of the g-string (hahahah) will you show regular expressions here? it would be a good time to at least make people they are here. what about templates? you could show string getAt() overloaded operator def s = "this is a string" s[1] s[1..3] s[-1..-3]def today = new Date() // Sat Apr 12 09:22:10 EDT 2014 // java.util.Date def day = new Date('05/19/2014') //Mon May 19 00:00:00 EDT 2014
def today = new Date() println today.format('MM/dd/yyyy') // 04-12-2014 // java.lang.String def today = new Date() today.format('MM-dd-yyyy hh:mm a') // 04-12-2014 09:31 AM
def today = new Date() def yesterday = today - 1 def tomorrow = today + 1 // Sat Apr 12 09:33:01 EDT 2014 // Fri Apr 11 09:33:01 EDT 2014 // Sun Apr 13 09:33:01 EDT 2014
Groovy ways to iterate through 2 dates
Date start = new Date() Date end = new Date() + 7 start.upto(end) { d -> println d } end.downto(start) { d -> println d } (start..end).each { d -> println d }
import groovy.time.TimeCategory def now = new Date() println now // Sat Apr 12 09:38:11 EDT 2014 use( TimeCategory ) { println 10.minute.from.now // Sat Apr 12 09:48:11 EDT 2014 println 10.hours.ago // Fri Apr 11 23:38:11 EDT 2014 println 10.weeks.from.now // Sat Jun 21 00:00:00 EDT 2014 println 3.days.ago // Wed Apr 09 00:00:00 EDT 2014 println 1.year.from.now // Sun Apr 12 00:00:00 EDT 2015 }Apply a number of methods to allow convenient Date/Time manipulation
def range = 1..10 println range // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] def reverseRange = 10..1 println reverseRange // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] println range.class.name // groovy.lang.IntRange
def range = 1..10 println range.size() // 10 println range.get(2) // 3 println range.contains(5) // true println range.from // 1 println range.to // 10 println range instanceof java.util.List // true
def range = 'a'..'p' println range // [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]
enum Days { MONDAY,TUESDAY,WEDNESDAY,THURSDAY, FRIDAY,SATURDAY,SUNDAY } def workWeek = Days.MONDAY..Days.FRIDAY def weekend = Days.SATURDAY..Days.SUNDAY def week = Days.MONDAY..Days.SUNDAY println workWeek println weekend println week // [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY] // [SATURDAY, SUNDAY] // [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]An enumeration is a collection of items that is a complete, ordered listing of all of the items in that collection.
def list = [] println list.class.name //java.util.ArrayList def list = [1,2,3,4,5] println list // [1, 2, 3, 4, 5] def list = [1,"two",3,"four",new Date(),["Mon","Tue","Wed"]] println list // [1, two, 3, four, Sat Apr 12 13:26:16 EDT 2014, [Mon, Tue, Wed]]Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null.
def list = [] list.add(1) list.putAt(1,2) //equivalent method to [] when value being changed list[2] = 3 list.push(4) list << 5 assert list == [1,2,3,4,5]
def list = [1,2,3,4,5] assert list[1] == 2 // indexing starts at 0 assert list.getAt(1) == 2 //equivalent method to [] assert list.get(1) == 2 //alternative method assert list[-1] == 5 //use negative indices to count from the end
def list = [1,2,3,4,5] assert list.remove(2) assert list == [1,2,4,5] list.pop() assert list == [1,2,4] list.clear() assert list == []In groovy, the bitwise operators can be overridden with the leftShift (<<) and rightShift (>>) methods defined on the class. it's idiomatic groovy to use the leftShift method for append actions on strings, buffers, streams, arrays, etc and thats what you're seeing here.
def map = [:] // map.foo will always look for property foo so class will never work here println map.class.name // java.util.LinkedHashMap println map.getClass().name
def map = [first:'Dan',last:'Vega',email:'danvega@gmail.com'] println map // [first:Dan, last:Vega, email:danvega@gmail.com]
assert [1:'a', 2:'b', 1:'c' ] == [1:'c', 2:'b'] //keys uniqueHash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order).
Maps can be made up of complex data
class Person { String first String last } def p = new Person(first:"Dan",last:"Vega") def map = [ key:"value", numbers: [1,2,3,4,5], person: p, now: new Date() ] println map [key:value, numbers:[1, 2, 3, 4, 5], person:Person@60aed478, now:Sat Apr 12 15:40:19 EDT 2014]
def map = [first:"Dan",last:"Vega"] def keys = map.keySet() println keys[0].class.name //java.lang.String println keys[1].class.name //java.lang.String
If you really want a variable to become the key, you have to wrap it between parentheses: [(a):1]
def a = 1 def map = [first:"Dan",last:"Vega",(a):new Date()] def keys = map.keySet() println keys[0].class.name //java.lang.String println keys[1].class.name //java.lang.String println keys[2].class.name //java.lang.Integer
def map = [first:"Dan",last:"Vega",email:'danvega@gmail.com'] map.first = "Daniel" assert map.first == "Daniel" map["first"] = "Dan" assert map.first == "Dan" assert map["first"] == "Dan" assert map.get('first') == "Dan" assert map.get('fullName','Dan Vega') == "Dan Vega"
We can check the contents of a map with various methods
def map = [first:"Dan",last:"Vega",email:'danvega@gmail.com'] assert [:].isEmpty() assert !map.isEmpty() assert map.containsKey('email') assert map.containsValue('danvega@gmail.com')
def a = true def b = true def c = false assert a assert a && b assert a || c assert !c
Empty collections are coerced to false.
def numbers = [1,2,3] assert numbers //true, as numbers is not empty numbers = [] assert !numbers //true, as numbers is now an empty collection
Iterators and Enumerations with no further elements are coerced to false.
assert ![].iterator() // false because the Iterator is empty assert [0].iterator() // true because the Iterator has a next element def v = new Vector() assert !v.elements() // false because the Enumeration is empty v.add(new Object()) assert v.elements() // true because the Enumeration has more elements
Non-empty maps are coerced to true.
assert ['one':1] assert ![:]
Matching regex patterns are coerced to true.
assert ('Hello World' =~ /World/) //true as matcher has at least one match
Non-empty Strings, GStrings and CharSequences are coerced to true.
// Strings assert 'This is true' assert !'' //GStrings def s = '' assert !("$s") s = 'x' assert ("$s")
Non-zero numbers are coerced to true.
assert !0 //yeah, 0s are false, like in Perl assert 1 //this is also true for all other number types
Non-null object references are coerced to true.
assert new Object() assert !null
null does not implicitly coerce to anything, it can only be used as a state test against other values
assert null == false //Will fail assert null == true //Also fails assert !null == true //These test will pass as we're testing against state assert !!null == false
if( groovyIsAwesome ) println( 'WOOT!' ) else youMustBeLying()
Nothing new or exciting here...
Lead into the next slide with a talk about how we're all used to if/else statements, etc...The switch statement in Groovy does allow us to do more than we're used to with ColdFusion
def x = 'This is interesting' switch( x ) { case ~/(?i)\bthis.*/: println 'first' case String: println 'second' case 'This is interesting': println 'third' break }Go over the verbosity of the switch statement and run the live example
Groovy supports syntax that seems more natural for looping
0.upto(4) { println( 'Hello World!' ) } 4.times { println( 'Hi World!' ) }
it also supports some syntax we're not used to seeing
for( x in 0..1 ) {println 'hi'}
as well as some things that should feel right at home
def x = [ 1:'dog', 2:'cat', 3:'bird' ] for( i in x ) { println i.value }
The elvis operator
y = x ?: zis the equivelant ofy = x ? x : zthat seems really neat (I know! Right?) but there are quite a few fun operators Groovy brings to the table
Groovy comes with a _ton_ of operators predefined you can also overload
http://groovy.codehaus.org/Operator+Overloadingdef x = 'Hello World!' def y = x - 'World!' println yLive example adding a plus method to custom classes http://groovy.codehaus.org/Operator+Overloading
def myNumbers = [1,2,3,4,5,6,7,8,9,10] def totalNumbers(numbers){ def total = 0; for(number in numbers){ total += number } total } // 55 println totalNumbers(myNumbers)Start with a scenario, you're working on an application and your boss comes to you with a request. We need a function that can total any numbers that are passed to it.
def myNumbers = [1,2,3,4,5,6,7,8,9,10] def totalEvenNumbers(numbers){ def total = 0; for(number in numbers){ if(number % 2 == 0) total += number } total } // 30 println totalEvenNumbers(myNumbers)
def myNumbers = [1,2,3,4,5,6,7,8,9,10] def totalOddNumbers(numbers){ def total = 0; for(number in numbers){ if(number % 2 != 0) total += number } total } // 30 println totalOddNumbers(myNumbers)
def myNumbers = [1,2,3,4,5,6,7,8,9,10] def totalNumbers(numbers, selector){ def total = 0; for(number in numbers){ if(selector(number)){ total += number } } total } println totalNumbers(myNumbers, { true } ) // 55 println totalNumbers(myNumbers, { it % 2 == 0 } ) // 30 println totalNumbers(myNumbers, { it % 2 != 0 } ) // 25
println totalNumbers(myNumbers) { true } // 55 println totalNumbers(myNumbers) { it % 2 == 0 } // 30 println totalNumbers(myNumbers) { it % 2 != 0 } // 25
// the times method accepts a closure as an argument 3.times( {println "Hello Closures!"} )
// If the last argument is a closure you can omit the parentheses 3.times {println "Hello Closures!"}
def myClosure = { println "Hello Closures!" } // we could call it directly myClosure() // or pass it around as a method argument 3.times(myClosure)A closure is a block of code that is a first class object that can be passed around.
3.times { // if i dont declare an argument list // the closure accepts 1 parameter and its called it println it } // 0 // 1 // 2
3.times { number -> println number }
3.times { int number -> println number }
printFullName = { firstName,lastName -> println "${firstName} ${lastName}" } printFullName("Dan","Vega")it is not a reservd word or keyword
Hopefully this code starts to make sense now
def list = ["one","two","three","four","five"] // loop over a list list.each { println it } def map = [first:"Dan",last:"Vega",company:"FirstComp"] // loop over a map map.each { key, value -> println "${key}: ${value}" }
Closures with prebound parameters are called curried closures. When we curry a closure, we're asking the parameters to be prebound.
def addNumbers = { x, y -> x + y } def addToTen = addNumbers.curry(10) assert 14 == addToTen(4)
addNumbers.rcurry(10) addNumbers.ncurry(index,obj)
class Foo { def closure = { println "${this.class.name},${owner.class.name},${delegate.class.name}" def innerClosure = { //delegate = owner.delegate println "${this.class.name},${owner.class.name},${delegate.class.name}" } innerClosure() } } def foo = new Foo().closure()
Groovy Closures support method delegation, and provide capabilities for method dispatching-much like JavaScript's support for prototypal inheritance.
class ClosureDemo { def foo() { def c = { write 'hello from foo.c' } c() } } def demo = new ClosureDemo() demo.foo()when groovy executes a closure it will try to resolve stuff first within the closure, then in the owner scope, and finally in the delegate. this ordering is only the default can be changed by setting the closures resolve strategy the ability to delegate is what makes DSL's possible and extremly powerful inside groovy
Meta-Programming is the writing of computer programs that write or manipulate other programs (or themselves).
Groovy impliments Meta-Programming at runtime with Meta Object Protocol and the ExpandoMetaClass
Every java.lang.Class is supplied with a special "metaClass" property that will give you a reference to an ExpandoMetaClass instance.
Straight from the source, here's the list of all the stuff you can do with the ExpandoMetaClass
class Developer { String firstName, lastName def makeExcuse(){ println "I'm compiling!" } } josh = new Developer( firstName : 'Joshua', lastName : 'Caito' ) josh.metaClass.isTeamLead = true gettingARaise = josh.isTeamLead ?: false println "Josh is getting a raise? ${gettingARaise}"
class Developer { String firstName, lastName def makeExcuse(){ println "I'm compiling!" } } josh = new Developer( firstName : 'Joshua', lastName : 'Caito' ) Developer.metaClass.work = { Integer hours -> println """I promise I'll work ${hours} hours today or my name isn't ${firstName} ${lastName}!""" } dan = new Developer( firstName : 'Dan', lastName : 'Vega' ) try{ josh.work(3) } catch( e ) { josh.makeExcuse() } dan.work(8)
class Manager { String firstName = 'Big' String lastName = 'Cheese' } boss = new Manager() try{ boss.work(3) } catch( e ) { println "I've delegated that, I swear!" } boss.metaClass.work = josh.&makeExcuse boss.work()
Using the ExpandoMetaClass, we can also invoke the methodMissing() method
This can be VERY useful
Thinking this is the first of a few code examples for this: class QA { String FirstName String LastName List expertise = [ 'Bugs', 'Issues', 'Errors' ] QA() { def mClass = new ExpandoMetaClass( QA, false, true ) //Add object, do not register, allow changes after init mClass.initialize() this.metaClass = mClass } def methodMissing(String methodName, arguments) { if( methodName.startsWith( 'locate' ) || methodName.startsWith( 'gripeAbout' ) ) { def listVar = methodName.startsWith( 'locate' ) ? methodName[6..-1] : methodName[10..-1] this.metaClass."$methodName" = {-> listVar + ' √' } listVar + ' √' } else { throw new MissingMethodException(methodName, this.class, arguments) } } } megan = new QA( firstName : 'Megan', lastName : 'Redacted' ) assert 'Bugs √' == megan.locateBugs() assert 'Issues √' == megan.gripeAboutIssues() assert 'Errors √' == megan.locateErrors() Found example code at: http://mrhaki.blogspot.com/2009/11/groovy-goodness-create-dynamic-methods.html and will be sure to credit Going to _really_ go through some examples here throughout and make sure people ask questions as they have them.... wondering if the closures conversation should go first just because of how much they are used here.... Also going to briefly cover DSLs and the whens and whys you'd want to use meta-programming I.E. don't just start mucking with every damn object as it's hard to trace when / how meta methods got registered.If you liked our presentation (or even if you didn't), please be sure to leave feedback!