How Groovy Helps
-
Upload
ken-kousen -
Category
Business
-
view
10.865 -
download
0
description
Transcript of How Groovy Helps
How Groovy Helps
Ken KousenKousen IT, Inc.
http://www.kousenit.com [email protected]
http://kousenit.wordpress.com
04/12/23 (c) 2008 by Kousen IT, Inc. 2
What is Groovy? From Scott Davis, Groovy Recipes:
Groovy is what Java would look like had it been written in the 21st century.
Groovy is a new breed of language. It doesn’t replace old technology as much as enhances it … this isn’t a “Hey guys, let’s rewrite our application from the ground up” … this is a “Let’s use a language that seamlessly integrates with our existing codebase” approach
04/12/23 (c) 2008 by Kousen IT, Inc. 3
Groovy Programming language with new features
Optional typing Closures Builders
Compiles to the JVM Compiled, not interpreted Interacts transparently with Java
If you don’t know the Groovy way, use Java Scripts as well as classes
Open source project
04/12/23 (c) 2008 by Kousen IT, Inc. 4
Groovy Features
Additions and simplifications to Java Special features of its own
Lists and Maps as part of the language Everything is an object Metaprogramming makes it easy to
create Domain Specific Languages Like Grails
04/12/23 (c) 2008 by Kousen IT, Inc. 5
Installing Groovy
Need a JVM first JDK 1.5+ preferred, others still work
Download Groovy from http://groovy.codehaus.org
Run installer (Windows) or just unzip and set GROOVY_HOME variable Add the bin directory to your path That’s all there is to it
04/12/23 (c) 2008 by Kousen IT, Inc. 6
Groovy Editors
Groovy comes with groovysh command line client groovyconsole Swing GUI
Plug-ins for most major IDEs Eclipse Netbeans JetGroovy plugin for IntelliJ IDEA
Favored by most of the core team members
04/12/23 (c) 2008 by Kousen IT, Inc. 7
Three Sample Problems
Google Chart is a web service that generates plots based on encoded data RESTful web service
Major League Baseball provides game information online XML processing and networking
Analyze training days data Database access, closures, and ranges
04/12/23 (c) 2008 by Kousen IT, Inc. 8
Google Chart API
The Google Chart API is a web service located at http://code.google.com/apis/chart/
So-called “RESTful” web service URL with query string to generate
response Lots of different chart types can be
generated Data must be “encoded”
04/12/23 (c) 2008 by Kousen IT, Inc. 9
Google Chart API Three types of encoding
Simple encoding uses alphanumeric characters A to Z, a to z, 0 to 9
62 unique values Text encoding uses floating point numbers
0.0 to 100.0 with a single decimal place 1000 unique values
Extended encoding uses pairs of alphanumeric characters
4096 unique values
04/12/23 (c) 2008 by Kousen IT, Inc. 10
Google Chart API Typical request:
http://chart.apis.google.com/chart?chs=200x125&chd=s:helloWorld&cht=lc&chxt=x,y&chxl=0:|Mar|Apr|May|June|July|1:||50+Kb
04/12/23 (c) 2008 by Kousen IT, Inc. 11
My Application Create pie chart of categories of training
classes delivered in 2007 To do so
Iterate over list of courses For each course, choose a category and
increment the number of courses in that category Encode the map into a string required by Google
Chart Create the required URL and access Google Chart
04/12/23 (c) 2008 by Kousen IT, Inc. 12
My Application More important, illustrate various
aspects of Groovy programming Groovy scripts and classes Lists and maps as native data structures Regular expressions Groovy Strings Ranges URL creation and access Groovy bean properties
04/12/23 (c) 2008 by Kousen IT, Inc. 13
Groovy Scripts and Classes
Groovy uses classes just as Java does Classes assumed to be public
Groovy also uses scripts Scripts become classes internally
Can mix script code and classes in the same source code file
04/12/23 (c) 2008 by Kousen IT, Inc. 14
google_chart.groovypackage chart
class Chart { def base = 'http://chart.apis.google.com/chart' def url = base + '?cht=p3&chs=500x150' def imageTag
// … more code to come …}
def chart = new Chart()chart.encodeMap()println chart.imageTag
04/12/23 (c) 2008 by Kousen IT, Inc. 15
Interesting Features No semicolons!
Actually, they’re optional Groovy code organized in packages Class is public by default
Fields are private by default Methods are public by default
Optional data typing def means untyped Can declare a type if you want
04/12/23 (c) 2008 by Kousen IT, Inc. 16
Interesting Features Strings can use either single or
double quotes Single quotes imply regular Strings Double quotes are “GStrings” and can
do variable replacement Script code shown in same file Accesses the imageTag property
Define a property, automatically get a public getter and a public setter method
04/12/23 (c) 2008 by Kousen IT, Inc. 17
More Fieldsdef data = [ 'Spring and Hibernate', 'EJB', 'J2EE', 'J2EE', 'Spring', 'XML', 'Spring', 'Spring', 'XML', 'XML', 'J2EE', 'WS', 'Struts', 'Spring', 'JS', 'WS', 'Ajax', 'WS', 'Portlets', 'Hibernate and EJB3', 'J2EE', 'OOAD', 'Java', 'Ajax', 'Ajax', 'Spring', 'Struts 2', 'XML and Java', 'Ajax and Java', 'Securing WS', 'JSF', 'Ajax', 'Ajax and Java', 'Ajax', 'J2EE', 'WS']
def map = [:]
04/12/23 (c) 2008 by Kousen IT, Inc. 18
Lists and Maps
Lists and Maps are native data structures
Lists declared using array syntax Default implementation is an ArrayList
Maps are defined using [key:value] Default implementation is a HashMap
04/12/23 (c) 2008 by Kousen IT, Inc. 19
Constructor CodeChart() { data.each { name -> if (name =~ (/Spring|Hibernate|Struts/)) { addToMap('Open Source') } if (name =~ (/Ajax|JS/)) { addToMap('Ajax') }
// … other categories … }}
04/12/23 (c) 2008 by Kousen IT, Inc. 20
Constructor Code List’s each method takes a Closure as an
argument Each element of the list is passed into the
closure as the variable name If closure is the last argument of a
method, can be placed after the method Closure is a block of code and the context
in which it runs Can be assigned to variables, passed into
methods, and so on
04/12/23 (c) 2008 by Kousen IT, Inc. 21
Constructor Code
Regular expression declared inside forward slashes Known as “slashy” syntax Any string can be defined that way
The =~ is the “find” operator Operator ==~ is the “match”
operator Operator ~ is the pattern operator
04/12/23 (c) 2008 by Kousen IT, Inc. 22
addToMap(label)
def addToMap(label) { map[label] = map.get(label,0) + 1}
04/12/23 (c) 2008 by Kousen IT, Inc. 23
addToMap(label) Method signature uses def as return type
Can return a value, or not Dummy argument doesn’t require a type
definition get(label,0)
Search for the value under the key label Return it if it exists, assign it with the default
value 0 if not Either way, increment the value and assign it
back into the map under the label key Can access a value using get method or
using array syntax map[label]
04/12/23 (c) 2008 by Kousen IT, Inc. 24
addToMap(label)
Resulting map of categories is [“Open Source”:9, “J2EE”:12, “Web Services”:9, “Ajax”:9, “Other”:1]
(some courses appear in more than one category)
04/12/23 (c) 2008 by Kousen IT, Inc. 25
Encoding Data values are all integers less than 64
Can always scale the data if not Use the Google Chart simple encoding
Values from 0 to 25 are the letters A to Z Values from 26 to 51 are the letters a to z Values from 52 to 61 are the numbers 0 to 9
Encode each value, then concatenate the results into a string which will be added as a query parameter
04/12/23 (c) 2008 by Kousen IT, Inc. 26
encodeMap()
def encodeMap() { List list = (('A'..'Z')+('a'..'z')+(0..9)) as List
String s = "&chd=s:" List keys = [] map.each { k,v -> keys << k s += list[v] }
// … more to come …
04/12/23 (c) 2008 by Kousen IT, Inc. 27
encodeMap() Values separated by two dots are Ranges Each range concatenated together The as operator “coerces” the Range into a List Could also use the toList() method
The each method iterates over map entries and passes each to the closure Closure declares keys as k, values as v Each k is appended to the keys list, using the <<
operator Values are used as an index to the range list,
which returns the proper encoded value
04/12/23 (c) 2008 by Kousen IT, Inc. 28
encodeMap()
String labels = "&chl=" + keys.collect { URLEncoder.encode(it,"UTF-8") }.join('|')
url += s // the query string, defined in the code aboveurl += labelsprintln url // might as well see itString urle = url.replaceAll(/&/,'&')imageTag = "<img src='${urle}' alt='Course distribution'/>"
04/12/23 (c) 2008 by Kousen IT, Inc. 29
encodeMap() Lots going on here Need to get the labels for the pie chart
The labels will be the keys from the map Encoding requires the labels to be
concatenated into a single string using | as a separator
The collect method passes each element of a collection through the closure Since no dummy variable was defined for the
closure this time, the default value “it” is used The src attribute set as a GString between ${…}
04/12/23 (c) 2008 by Kousen IT, Inc. 30
encodeMap() Each label is passed through the standard Java URLEncoder.encode() method
If you don’t have a specifically Groovy way to do something, use Java
Total URL is fine, but if it is the argument to the src attribute of an img tag, it can’t include &
Have to encode & using the entity reference & The replaceAll method replaces each string that
matches the regular expression with the supplied value
04/12/23 (c) 2008 by Kousen IT, Inc. 31
Result <img src='http://chart.apis.google.com/chart?cht=p3
&chs=500x150&chd=s:JMJJB&chl=Open+Source|J2EE|Web+Services|Ajax|
Other' alt='Course distribution'/> The chart type is p3 (a 3D pie chart) The chart size (chs) is 500x150 pixels The encoded chart data was JMJJB (9, 12, 9, 9, 1)
The s: indicates the simple encoding scheme The chart labels are in the URL encoded string after chl
04/12/23 (c) 2008 by Kousen IT, Inc. 32
Result
04/12/23 (c) 2008 by Kousen IT, Inc. 33
Google Chart Builder Zan Thrash has created a full builder called
the Google Chart Builder See his blog posting at
http://zanthrash.blogspot.com/2008/01/groovy-chart-builder.html
Source code is available and instructive Grails has a Google Chart plugin at
http://grails.org/Google+Chart+Plugin Either of those are excellent for real work
04/12/23 (c) 2008 by Kousen IT, Inc. 34
Baseball Data Publicly accessible in XML format Base URL is
http://gd2.mlb.com/components/game/mlb Games are filed by year, month, and day Game directory uses team abbreviations
2007 World Series Game 4: year_2007/month_10/day_28/gid_2007_10_28_bosmlb_colmlb_1/boxscore.xml
04/12/23 (c) 2008 by Kousen IT, Inc. 35
boxscore.xml Typical boxscore includes
Root element is <boxscore> Child elements
<linescore> <pitching> for home and away teams
<pitcher> element for each pitcher <batting> for home and away teams
<batter> element for each batter Assorted <note>, <text_data>, <game_info> elements
04/12/23 (c) 2008 by Kousen IT, Inc. 36
Java Approach Consider the Java code to access the
data Open a URL and a URLConnection Get a DocumentBuilderFactory Get a DocumentBuilder Invoke the parse method using an InputStream from the URLConnection
Either traverse the tree or call getElementByTagName to retrieve the data
Don’t forget all the try/catch blocks
04/12/23 (c) 2008 by Kousen IT, Inc. 37
Groovy Approach
Use XmlParser or XmlSlurper Very similar classes with mostly the
same methods Parser returns a list of nodes to navigate Slurper returns GPathResults, which are
like XPath but groovier Just to keep things simple, since the
data isn’t massive, we’ll use the parser
04/12/23 (c) 2008 by Kousen IT, Inc. 38
GetGameData.groovy
class GetGameData { def day def month def year def base = 'http://gd2.mlb.com/components/game/mlb/'
// all teams in actual file def abbrevs = [ ana:"Los Angeles (A)",ari:"Arizona",atl:"Atlanta", bal:"Baltimore",bos:"Boston",cha:"Chicago (A)", chn:"Chicago (N)",cin:"Cincinnati",cle:"Cleveland", col:"Colorado", was:"Washington"]
04/12/23 (c) 2008 by Kousen IT, Inc. 39
getGame()
void getGame(away, home, num) { println "$away at $home on ${month}/${day}/${year}" def url = base + "year_${year}/month_${month}/day_${day}/" def game = "gid_${year}_${month}_${day}_" + "${away}mlb_${home}mlb_${num}/boxscore.xml" def boxscore = new XmlParser().parse(url + game)
04/12/23 (c) 2008 by Kousen IT, Inc. 40
getGame()
def awayName = boxscore.'@away_fname'def awayScore = boxscore.linescore[0].'@away_team_runs'def homeName = boxscore.'@home_fname'def homeScore = boxscore.linescore[0].'@home_team_runs'println "$awayName $awayScore, $homeName $homeScore (game $num)"
04/12/23 (c) 2008 by Kousen IT, Inc. 41
getGame()
def pitchers = boxscore.pitching.pitcher pitchers.each { p -> if (p.'@note' && p.'@note' =~ /W|L|S/) { println " ${p.'@name'} ${p.'@note'}" } }
04/12/23 (c) 2008 by Kousen IT, Inc. 42
Interesting Features GStrings simplify string handling
Java would have to use concatenation to mix strings and variables
XmlParser has a parse method that takes a URL
Result is a DOM tree Child elements found using dot notation
boxscore.pitching.pitcher selects all pitchers Attribute values use @ notation
04/12/23 (c) 2008 by Kousen IT, Inc. 43
Result (so far)
bos at col on 10/28/2007Boston Red Sox 4, Colorado Rockies 3 (game 1) Lester (W, 1-0) Papelbon (S, 3) Cook (L, 0-1)
04/12/23 (c) 2008 by Kousen IT, Inc. 44
Process the Data
def batters = boxscore.batting.batterfor (b in batters) { println "${b.'@name'} went ${b.'@h'} for ${b.'@ab'}"}println batters.size() + " total batters"println 'Total hits: ' + batters.'@h'*.toInteger().sum()
04/12/23 (c) 2008 by Kousen IT, Inc. 45
Notes
Groovy prefers for/in loop Could use batters.each here instead
NodeList has size() method So does List, String, arrays, etc.
Spread-dot operator, *. Applies function after dot to each
element of a list
04/12/23 (c) 2008 by Kousen IT, Inc. 46
More Processing
println "Batters with at least one hit:" println batters.findAll { it.'@h'.toInteger() > 0 }.collect { it.'@name' + '(' + it.'@h' + ')' }}
04/12/23 (c) 2008 by Kousen IT, Inc. 47
List Processing List has a findAll method that takes a
closure Default reference to each element is “it” List also has collect method
Applies closure to each element and returns a list containing the results
Many other list processing methods available each, every, unique, sort, join, flatten, reverse, intersect, +, -
04/12/23 (c) 2008 by Kousen IT, Inc. 48
XML Builder
Groovy implements Builder pattern Uses metaprogramming to intercept
“pseudo” method calls Builders already included
NodeBuilder for XML AntBuilder for creating Ant scripts SwingBuilder for GUIs BuilderSupport for creating your own
04/12/23 (c) 2008 by Kousen IT, Inc. 49
Create HTML
void writeHtml(node) { def away = node.'@away_sname‘ def home = node.'@home_sname' def name = "$away at $home" new File("${away}${home}.html").withWriter { w -> def builder = new MarkupBuilder(w)
04/12/23 (c) 2008 by Kousen IT, Inc. 50
Notes
File has withWriter method that creates a Writer and automatically closes it when done
MarkupBuilder overloaded to take a Writer Default writes to standard out
04/12/23 (c) 2008 by Kousen IT, Inc. 51
Building HTML
builder.html { head { title name } body { h1 name h2 node.'@date' node.batting.each { batters -> def team = batters.'@team_flag' + '_sname' h2 node."@${team}"
04/12/23 (c) 2008 by Kousen IT, Inc. 52
Notes
Looks like HTML Each HTML term is intercepted
Builder then creates XML node with that name
Closure after node indicates child node Argument in parentheses is attribute
Can freely mix regular Groovy variables and the builder nodes
04/12/23 (c) 2008 by Kousen IT, Inc. 53
More HTML
table (border:'2') { tr { th 'Batter'; th 'AB'; th 'R'; th 'H' th 'RBI'; th 'BB'; th 'SO' } batters.batter.findAll { it.'@pos' != 'P' }.each { b -> tr { td b.'@name' + ' (' + b.'@pos' + ')' td b.'@ab'; td b.'@r'; td b.'@h' td b.'@rbi'; td b.'@bb'; td b.'@so' } }
04/12/23 (c) 2008 by Kousen IT, Inc. 54
Table, continued
tr { td(style:'font-weight:bold','Totals') td batters.batter.'@ab'*.toInteger().sum() td batters.batter.'@r'*.toInteger().sum() td batters.batter.'@h'*.toInteger().sum() td batters.batter.'@rbi'*.toInteger().sum() td batters.batter.'@bb'*.toInteger().sum() td batters.batter.'@so'*.toInteger().sum() }}
04/12/23 (c) 2008 by Kousen IT, Inc. 55
Results
HTML output file Generating XML just as easy Swing GUIs are fun, too
04/12/23 (c) 2008 by Kousen IT, Inc. 56
HTML Boxscore
04/12/23 (c) 2008 by Kousen IT, Inc. 57
Screen Scraping
Want all games for a given date MLB web page lists games in
subdirectories, as discussed above Can’t just use XML Parser
HTML not necessarily well-formed Without a schedule, don’t know which
games played on each day Grab page and screen scrape
04/12/23 (c) 2008 by Kousen IT, Inc. 58
Games for 5/5/2007
04/12/23 (c) 2008 by Kousen IT, Inc. 59
Find Games on Date
void getGames() { println "Games for ${month}/${day}/${year}" def url = base + "year_${year}/month_${month}/day_${day}/" def gamePage = new URL(url).text def pattern = /\"gid_${year}_${month}_${day}_(\w*)mlb_(\w*)mlb_(\d)/
04/12/23 (c) 2008 by Kousen IT, Inc. 60
Notes URL class has getText() method
Invoked using text property Retrieves entire page for processing
Could use eachLine() method instead Processes line by line Available in any File Works for URLs, too, by duck typing
Each game in a link with the above pattern Also in text, so include the leading quotes to
distinguish between the two Groups (\w*) contain the team abbreviations
04/12/23 (c) 2008 by Kousen IT, Inc. 61
Process HTML Pagedef m = gamePage =~ patternif (m) { (0..<m.count).eachWithIndex { line, i -> def away = m[line][1] def home = m[line][2] def num = m[line][3]
try { getGame(away,home,num) } catch (Exception e) { println abbrevs[away] + " at " + abbrevs[home] + " not started yet"} } }
04/12/23 (c) 2008 by Kousen IT, Inc. 62
Notes
Matcher’s count property tells how many matches
Range (0..<x) is from 0 up to but not including x
Iterator eachWithIndex available Index not actually used here, just
shown for illustration
04/12/23 (c) 2008 by Kousen IT, Inc. 63
ResultsGames for 05/05/2007Boston at Minnesota on 05/05/2007Boston Red Sox 1, Minnesota Twins 2 (game 1) Tavarez (L, 1-3) Santana (W, 4-2) Nathan (S, 8)Chicago (A) at Los Angeles (A) on 05/05/2007Chicago White Sox 6, Los Angeles Angels 3 (game 1) Garland (W, 1-2) Jenks (S, 9) Lackey (L, 4-3)…
04/12/23 (c) 2008 by Kousen IT, Inc. 64
Database Data
Groovy simplifies database access groovy.sql.Sql class
Can do inserts, updates, deletes, and queries
Includes many convenience methods
04/12/23 (c) 2008 by Kousen IT, Inc. 65
Training Data Sample data from training company
Want to determine teaching days and revenue Three tables
Location id, city, state, version (for optimistic locking) latitude, longitude (computed from web service)
Client id, name, version
Course id, title, start_date, end_date, rate, version
location_id FK to Location.id client_id FK to Client.id
04/12/23 (c) 2008 by Kousen IT, Inc. 66
Training Data Data stored in MySQL database
Use MySQL JDBC driver Note length of course is dependent
variable Computed from start_date and end_date
Revenue adding in rate for each day, since course may have calendar gaps
Lots of annoying Java calendar processing
04/12/23 (c) 2008 by Kousen IT, Inc. 67
Side Issues Initial insert into database done by
collecting data in spreadsheet Can access spreadsheet directly using
Scriptom library Alternative is to save as CSV and process
file Need latitude and longitude for each
city, state in order to create Google Map
04/12/23 (c) 2008 by Kousen IT, Inc. 68
Sample CSV Data
75,5/14/2007,5/18/2007,Java Web Services,Lancaster,PA76,5/21/2007,5/25/2007,Ajax and Struts,Philadelphia,PA77,6/4/2007,6/12/2007,Java Web Services,McLean,VA78,6/25/2007,6/29/2007,Portal Dev. Using RAD6,Enola,PA80,7/9/2007,7/14/2007,Hibernate EJB3,Atlanta,GA81,8/23/2007,8/23/2007,J2EE for Managers,Marlborough,CT
04/12/23 (c) 2008 by Kousen IT, Inc. 69
Process CSV Data
class Record { int num; String startDate; String endDate String title; String city; String state
Record(String[] s) { num = s[0].startsWith("#") ? 0 : s[0].toInteger() startDate = s[1]; endDate = s[2] title = s[3]; city = s[4]; state = s[6] } String toString() { "$title ($startDate -- $endDate) $city, $state"} }
04/12/23 (c) 2008 by Kousen IT, Inc. 70
Process CSV Datadef records = []try { def dataFile = new File('Course_locations.csv') def lines = dataFile.readLines() for (line in lines) { elements = line.trim().split(',') records << new Record(*elements) }} catch (FileNotFoundException e) { println e}records.each { println it }
04/12/23 (c) 2008 by Kousen IT, Inc. 71
GroovyBeans
GroovyBeans declare properties Put in fields, accessors automatically
generated (Groovy calls both gets and sets
accessors) Map constructor generated, too
new Record(title:”Groovy”,…) assigns value to title, etc.
04/12/23 (c) 2008 by Kousen IT, Inc. 72
Notes File has readLines method
Closure argument applies to each line Can use String methods like split and trim
Spread operator takes list and splits it into input arguments Again, not really necessary here since
constructor takes an array, but interesting nevertheless
Note toString method does not require a return keyword Value of closure returned by default
04/12/23 (c) 2008 by Kousen IT, Inc. 73
Geocoding
Geocoders available as web services Google has one So does the USGS
Return information as XML or CSV
04/12/23 (c) 2008 by Kousen IT, Inc. 74
Using Google Geocoderdef key = ‘long_ugly_key'def base = 'http://maps.google.com/maps/geo'def city = 'Marlborough'def state = 'CT'
def query = "q=${city},+${state}&output=csv&key=${key}"def url_string = base + '?q=' + ',+' + URLEncoder.encode(city,"UTF-8") + ',+' + state + "&output=csv&key=${key}"def results = new URL(url_string).text.split(',')def latitude = results[-2].toDouble();def longitude = results[-1].toDouble();println "(${latitude},${longitude})"assert "(41.63256,-72.46315) == (${latitude},${longitude})"
04/12/23 (c) 2008 by Kousen IT, Inc. 75
Google Geocoder
When csv parameter included, result is four strings HTTP status code (hopefully 200) Accuracy (magnification level) Latitude Longitude
Note use in script of indices -1, -2 to grab two elements from the end
04/12/23 (c) 2008 by Kousen IT, Inc. 76
USGS Geocoder
def partialCsvRequest = "http://geocoder.us/service/csv/geocode?address="def address = "11 Emily Road, Marlborough, CT"def csvUrl = new URL(partialCsvRequest + URLEncoder.encode(address))def csvResponse = csvUrl.textdef tokens = csvResponse.split("," )println "Latitude: [${tokens[0]}]"println "Longitude: [${tokens[1]}]"println "Address: [${tokens[2]}]"println "City: [${tokens[3]}]"println "State: [${tokens[4]}]"println "Zip: [${tokens[5]}]"
04/12/23 (c) 2008 by Kousen IT, Inc. 77
TrainingCourse
class TrainingCourse { String title; Date startDate; Date endDate; double rate
String toString() { def nf = NumberFormat.currencyInstance "(${title},(${startDate} - ${endDate}),${nf.format(rate)})" }
int getLength() { (startDate..endDate).size() }
double getRevenue() { length * rate }}
04/12/23 (c) 2008 by Kousen IT, Inc. 78
Notes
Can provide get methods as necessary
Dates are a range now..then size() returns number of days Can add, subtract days
04/12/23 (c) 2008 by Kousen IT, Inc. 79
CourseProcessor.groovyclass CourseProcessor { def courses = [] def courseYearMap = new TreeMap() def cal = Calendar.instance
CourseProcessor() { def USER = ‘xxx‘; def PASS = ‘xxx’; def URL = ‘xxx‘; def DRIVER = ‘xxx' def conn = Sql.newInstance(URL,USER,PASS,DRIVER) conn.eachRow('select * from course') { row -> courses << new TrainingCourse( title:row.title,startDate:row.start_date, endDate:row.end_date,rate:row.rate) }
04/12/23 (c) 2008 by Kousen IT, Inc. 80
Continuing…
courses.each { c -> cal.time = c.startDate def year = cal.get(Calendar.YEAR) courseYearMap[year] = courseYearMap.get(year,[]) + c }}
04/12/23 (c) 2008 by Kousen IT, Inc. 81
Notes
Want to sort courses by year Current approach is to add them to a
map Using TreeMap instead of [:] means
courses automatically sorted by key on add
Map contains Years as keys List of courses as values
04/12/23 (c) 2008 by Kousen IT, Inc. 82
Printing Courses
def printYearMap() { def nf = NumberFormat.currencyInstance courseYearMap.each { year,courseList -> def monthlyDays = new int[12] def monthlyRev = new double[12] courseList.each { course -> cal.time = course.startDate def month = cal.get(Calendar.MONTH) for (date in course.startDate..course.endDate) { monthlyDays[month] += 1 monthlyRev[month] += course.rate } }
04/12/23 (c) 2008 by Kousen IT, Inc. 83
Continuing
def days = (courseList*.length).sum()def rev = (courseList*.revenue).sum()println "Totals for $year : ${days} days, ${nf.format(rev)}"
04/12/23 (c) 2008 by Kousen IT, Inc. 84
Testing Testing
assert keyword used everywhere GroovyTestCase extends JUnit TestCase
assertArrayEquals assertContains(int, int[]) assertLength assertScript(String script)
Asserts script runs without exceptions shouldFail(Closure) assert closure fails when
run
04/12/23 (c) 2008 by Kousen IT, Inc. 85
Testing
Can execute any GroovyTestCase as regular Groovy script Runs as though it has main method
Mocks and Stubs built in, too
04/12/23 (c) 2008 by Kousen IT, Inc. 86
Miscellaneous I see I’ve left out lots of cool topics
Safe dereferencing operator ?. a?.b?.c checks for null a and b
Templates look like JSPs, but run with Groovy scripts Will discuss with Grails
Metaprogramming (the red pill) Great for creating DSLs
Heredocs def doc = '''This is
a multiline script written over several lines''‘
Grails!
04/12/23 (c) 2008 by Kousen IT, Inc. 87
References
Groovy home is http://groovy.codehaus.org Lots of info, plug-ins, details Mailing lists at
http://groovy.codehaus.org/Mailing+Lists News portal at http://aboutgroovy.com Lots of samples at
http://pleac.sourceforge.net/pleac_groovy/