FlexUnit 4 Beta 2 Released

Brian LeGros | August 24th, 2009 | news  

Today the 2nd beta release of FlexUnit4 was published. This release comes with a large list of bug fixes and improvements. This past weekend I burned the midnight oil with Mike and we buttoned up the FlexUnit4AntTasks project and I threw together a new sample project for FlexUnit4 showing how to integrate with Ant and Maven (via the Flex-Mojos plugin). Currently xvfb support isn’t available but we hope to add it in the next beta along with a few bug fixes and refactorings that we have time to finish. If you’re running your CI server on Windows using the Local System account, you should be able to take advantage of the new FlexUnit Ant task; the MacOS X and Linux crowd will have to work with source for now.

If your interested in more details, check out Mike’s latest blog post on the Beta 2 release.

Experiences from using Grails to migrate legacy web services

Brian LeGros | August 15th, 2009 | programming  

Recently I was tasked with migrating a version of PHP web services to our new and growing Java stack. I had to maintain the current XML interface over HTTP but use our Java services behind the scenes to satisfy the requests. In an effort to make things easier on myself, I decided to use Grails because we were using a fairly typical Java stack of technologies (Spring, Hibernate, JUnit, etc.) that integrate extremely well with Grails’ underlying architecture. Basically, I had everything I needed in terms of persistence, domain, validation, etc in our existing stack, I just had to use Grails to expose it with a specific XML interface. Now that the proof of concept phase is complete for the project, I figured I’d share some of the niceties and challenges I encountered while approaching this problem with Grails.

First off, the expressiveness of Groovy as a language (I used 1.6.3) made the biggest task of mapping a very rigid, existing XML interface to our internal domain much easier and shorter than if I would have written it in Java. The messages were basically a flattening/expanding of our internal object graph with a couple of really quirky translations of data. Being able to treat literals as objects and the use of collection operators (*.) and methods (collect, each, find) saved me quite few lines of code. On the testing side of things, strictly from the perspective of Groovy, the as operator just kicks ass. Since mapping these interfaces was my focus, I didn’t find the need to mock too many expectations from our services, so stubbing out dependencies using maps and closures made testing SO much easier. I did find a down side to stubbing directly in Groovy however. If you are attempting to stub out a class without a no-arg constructor, you’re going to have to use a library like GMock which will allow you to create a stub passing constructor arguments. I’m not too partial to play, rewind type mocking frameworks, so I was a little bummed to have to pull in another resource to test, but it got the job done.

On the Grails side of things, integration with our existing services couldn’t have been easier. We used Grails 1.1.1 for our implementation. With a few tweaks to resources.xml, I was able to reuse our entire set of bean declarations and have them injected by name into our controllers. Although, we didn’t place our Hibernate mapping files in the /hibernate folder, I was able to create my own session factory with relative ease to get Hibernate up and running. Once I had this working, I knew I could eventually take advantage of the real reason I wanted to use Grails, RESTful interfaces over HTTP. The former PHP interface implemented a Front-Page Controller for all of its services, so using URLMappings I was able to emulate index.php to the rest of the world pretty easily. On a side note, the former interface was expecting a request variable name action which Grails reserves, but using a simple closure in UrlMappings allowed me to map it as a new request variable name and go on my way. In any case, since this is a specific endpoint, I am left open to create new controllers using Grails’ REST support as we migrate away from the older interfaces. This also means, from what I understand (thanks implicit GORM support from setting up Hibernate), I can start to use xml property binding to create my domain objects and the encodeAsXml() and encodeAsJson methods to serialize those object across the wire. This is pretty huge for us and has definitely improved my opinion of working with Java as a langauge on the web. It also solidified for me that Grails has matured quite a bit since I last gave it a test run. Lastly, I have to say that I’m really digging the simplicity of functional-testing plugin. Initially I had looked at WebTest, but this plugin is much less intimidating and was dirt simple to get started with.

Let me just stop hear and say that there were quite a few challenges to bringing Grails into our stack as well. The challenges weren’t particularly due to the implementation code itself but more on the supplemental side of things in terms of testing, continuous integration, and IDE support. Coming from an existing Java code base, we already had a set of testing tools we liked based on JUnit 4, JMock, and Spring testing. This included some really productive tools we built around dbunit supporting yaml for integration testing. Currently, Grails only supports JUnit 3, so ideally test cases should extend GroovyTestCase; there is also GrailsUnitTestCase which helps out with the mocking story quite a bit on unit tests for domain classes. Having to use JUnit 3 meant making a decision to go back and refactor testing support to work with both JUnit 4 and JUnit 3 or just rely more heavily on the functional testing support in Grails. Since I was just building controllers and a couple of domain classes, I chose to rely more on functional testing. For what its worth, there is a ticket out there for JUnit4 support. JUnit4 support aside, probably one of the most detracting things from testing with Grails is the tedium involved when testing controllers. Grails will inject a MockHttpServletRequest, MockHttpServletResponse, and MockHttpSession object for use by your controllers in the test case so you check redirected/forwarded urls, what responses will look like, easily create request edge cases, etc. By executing grails test-app -integration you can quickly see what I’m talking about. The injection process is painfully slow, at least on my last gen Macbook Pro, and I feel like I am always sitting, waiting around for these tests. I could have probably thinned down my controllers a bit, but most of the cost of executing these tests was on startup of the overall run. The only other thing I would have like to seen supported is the ability to have multiple test classes for the same domain, service, controller class. The permutations of response messages from each controller action were quite numerous, so I would have liked to create a test case per controller action rather than just one for the entire controller for readability. I only had luck getting a single test case to execute following the Grails naming conventions per object.

On the CI side of things, we build our Java projects using Maven. For large Java applications, we’ve been unable to find a simpler approach to build our software than using multi-module projects in Maven. Grails’ Maven support unfortunately did not work well with our multi-module project although it looks like they’ve made some updates for a quick fix that may help. Until then, I have our project POM hacked together a bit marking the executions of the plugin myself and using the dependency plugin to create a lib directory so I can work with grails outside of Maven w/o issues. Here are some of the relevant changes I made for my POM:

...
   <build>
      <plugins>
         <plugin>
            <groupId>org.grails</groupId>
            <artifactId>grails-maven-plugin</artifactId>
            <version>1.1-SNAPSHOT</version>
            <extensions>true</extensions>
            <executions>
               <execution>
                  <id>grails-clean</id>
                  <goals>
                     <goal>clean</goal>
                  </goals>
                  <phase>clean</phase>
               </execution>
               <execution>
                  <id>grails-test-app</id>
                  <goals>
                     <goal>test-app</goal>
                  </goals>
                  <phase>test</phase>
               </execution>
               <execution>
                  <id>grails-war</id>
                  <goals>
                     <goal>war</goal>
                  </goals>
                  <phase>package</phase>
               </execution>
            </executions>
            <dependencies>
               <dependency>
                  <groupId>org.codehaus.groovy</groupId>
                  <artifactId>groovy</artifactId>
                  <version>1.6.3</version>
               </dependency>
            </dependencies>
         </plugin>
         <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
               <execution>
                  <id>copy-grails-test-results-for-ci</id>
                  <phase>test</phase>
                  <configuration>
                     <tasks>
                        <property name="report.loc" location="${project.build.directory}/surefire-reports" />
                        <mkdir dir="${report.loc}" />
                        <copy todir="${report.loc}">
                           <fileset dir="${project.build.directory}/../test/reports">
                              <include name="**/TEST-*.xml" />
                              <exclude name="TEST-TestSuites.xml" />
                           </fileset>
                        </copy>
                     </tasks>
                  </configuration>
                  <goals>
                     <goal>run</goal>
                  </goals>
               </execution>
               <execution>
                  <id>copy-grails-war-to-target-for-lifecycle</id>
                  <phase>package</phase>
                  <configuration>
                     <tasks>
                        <copy todir="${project.build.directory}">
                           <fileset dir="${project.build.directory}/..">
                              <include name="*.war" />
                           </fileset>
                        </copy>
                     </tasks>
                  </configuration>
                  <goals>
                     <goal>run</goal>
                  </goals>
               </execution>
            </executions>
         </plugin>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-clean-plugin</artifactId>
            <version>2.3</version>
            <executions>
               <execution>
                  <id>clean-dependencies-in-lib-for-clean</id>
                  <phase>pre-clean</phase>
                  <goals>
                     <goal>clean</goal>
                  </goals>
                  <configuration>
                     <filesets>
                        <fileset>
                           <directory>${basedir}/lib</directory>
                           <includes>
                              <include>**/*.jar</include>
                           </includes>
                        </fileset>
                     </filesets>
                  </configuration>
               </execution>
            </executions>
         </plugin>
         <plugin>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
               <execution>
                  <id>copy-dependencies-into-lib</id>
                  <phase>generate-sources</phase>
                  <goals>
                     <goal>copy-dependencies</goal>
                  </goals>
                  <configuration>
                     <outputDirectory>${basedir}/lib</outputDirectory>
                     <excludeGroupIds>org.grails,org.codehaus.groovy</excludeGroupIds>
                  </configuration>
               </execution>
            </executions>
         </plugin>
         ...
      </plugins>
   </build>
...

In terms of CI server integration, we use Hudson and the only hiccup I ran into was to make sure the user the app server is running as has rights to create a /.grails folder in its home directory. It would be nice to be able to run the unit and integration tests with lifecycle rather than all at once (GRAILS-4569) as well as have a goal to just run functional tests, but I know they are working on it. It looks like a lot of work has been done with this plugin since the 1.1-SNAPSHOT we were using, so I’m eager to give it another try as we add more web services to the mix.

The last hurdle I had to overcome in using Groovy and Grails was IDE support. Working with Java and Flex, I’ve come to find that Eclipse is my staple development tool. Unfortunately, working with the combination of Maven, Groovy, and Grails, I found myself continually frustrated with the Groovy Eclipse plugin. m2eclipse really didn’t help either since a Grails application directory structure doesn’t match the traditional Maven project layout. In the end I found myself just using the code highlighting and limited refactoring support in Groovy Eclipse and then command-line for everything Grails related. I’d heard a lot about Netbeans 6.7 support for Grails, so I was optimistic, but the experience was more of a honeymoon than anything else. Netbeans has great Maven support, but unfortunately Grails projects in Netbeans can’t take advantage of that support; Netbeans doesn’t seem to have project nature support like Eclipse. I found myself using the built-in context menus for the Grails commands to keep me from going out to the command line, but this was pretty much the only feature I could justify using in Netbeans. The code completion support was far from responsive. I found myself waiting almost 30s in some cases for the “Please wait…” pop-up to only find that the Groovy editor couldn’t tell me anything about my typed/untyped variable other than the defaults. The Subversion client is terrible. When committing, each file in the changeset has a drop-down box for what you’d like to do with the file (exclude, add, commit) and the default is add or commit. If you have a working copy with lots of files you want to exclude (e.g. – artifacts from a build process) you have to one-by-one exclude each file. Also, I’m not sure why but the patch command is not available from the SVN context-menu on the project or files view. I had to hunt through the Team menu under Subversion to finally find the option. Subversive and Subclipse for Eclipse are a reason enough to avoid Netbeans if you’re an SVN user, IMO. Maybe I couldn’t find the hotkey or context menu option, but there also didn’t seem to be the ability to refresh a folder on the project or files view? I noticed that Netbeans was constantly “scanning folders” to find changes on the file system but I had no manual way of just saying refresh. Consequently I had a lot of folder sync’ing issues between the file system and Netbeans in the rare case I needed to use the command line. In the end, a lot of the day-to-day niceties I had become accustom to just weren’t in Netbeans and for the way I was using it, it just wasn’t for me. I had heard really good things about the IntelliJ support for Grails, but due to a license fee, my poor experience with Netbeans, and the fact that Eclipse does everything else I need for development, I ended up just sticking with Eclipse. On a more positive note, SpringSource has released an alpha build of a new Groovy plugin for Eclipse which has some great promise. There is also talk of STS for Eclipse adding Grails support, so I’m hopeful the Groovy/Grails IDE experience can only improve from here.

Overall, despite the challenges I encountered, Grails is a dynamite web framework if you’re already working with Spring and Hibernate in your stack. I’ll acknowledge I may be a complete n00b in terms of the learning curve still, so if I’ve totally missed the boat on some of my points, please let me know so I can update this post. I’m excited to continue using Grails, especially with the newer releases coming up which will including Spring 3.0 and Spring-Flex.

Code and slides finally posted from FlexCamp Miami

Brian LeGros | March 29th, 2009 | conferences  

Sorry to everyone who’s been asking about a copy of my presentation from FlexCamp Miami. I’ve just posted the source and slides for my presentation @ http://svn.adogo.us/200903-FlexCampMiami/ along with Max, who had his stuff up pretty quickly after the conference. I wanted to take some time to add a better example of an integration test which is now available in the RestaurantGrid component project. What delayed me was finding an easy way to explain how to test the DataGrid embedded within the RestaurantGrid; I tried to provide the most basic of examples with and without the help of the Flex Automation API.

Hope this helps those who were interested; sorry again about the delay. If you have any questions, please don’t hesitate to contact me via comments or email me @ me at brianlegros dot com. Also, if you find yourself in the Orlando area, always feel free to drop by an Adogo meeting. This month we’ve got a great JavaScript topic being presented by Adam as well as Max giving his FlexCamp Miami presentation with, if we’re lucky, working AOP in AS3 via Loom! Check out the Adogo blog for more information on time and location. Hope you can make it!

Fluint 1.1.0 Released!

Brian LeGros | February 12th, 2009 | programming  

Over the last few months, I’ve been working with Michael Labriola on the next minor version release of Fluint. Well, this evening Michael and I put the finishing touches on release 1.1.0. This release brought all of the changes I made to get Fluint working at the office for our CI process to the community. There are a couple of cool little gems that I think will be helpful for some:

  • Separation of failures and errors in test reporting (visual and XML).
  • XML compliant output with most CI servers as well as the JUnitReport task in Ant and the Surefire Reporting plugin in Maven.
  • Support in the Ant task for truly headless executions of the AIR Test Runner via xvfb-run.
  • Support for relative paths in the Ant task and AIR Test Runner.
  • Better error handling for non-compliant modules loaded into the AIR Test Runner.
  • LogBook integration for debugging
  • A big folder re-organization and Ant scripts to support developers when they need specialty builds.
  • Agreed upon a branching and tagging strategy so the community can have stable snapshots of source to build.

We probably should put together a ChangeLog wiki page, so that may be available soon. We did a few updates to the wiki, so stop by the Google Code site and check out the changes. Also, provide feedback on the discussion list, if there are features you’d like to see implemented, bugs that you find, or just general questions you need help with. People are great about helping on the list, so don’t feel discouraged.

This is my first major release of an OSS project that I’ve had code go into, so thanks to everyone who helped with the release, especially my wife for putting up with a lot of late nights. There is a lot more to come with Fluint over the next year that is going to be really exciting. Look for announcements during my presentation at FlexCamp Miami and on the discussion mailing list. Hope you enjoy the new release!

Continuous Integration with Maven, Flex, Fliunt, and Hudson

Brian LeGros | December 17th, 2008 | programming  

Recently I was tasked with streamlining our build process at work so we could get a continuous integration (CI) server up and running. We use the common stack of technologies found in most Flex shops (basic SDK, some libraries, and Flex Builder) as well as Maven. I ran into some challenges getting our CI process to work as we wanted, so I figured I’d go through some of the gotchas I encountered.

On the build side of things, when I came on, Maven was already in place using flex-mojos. Now I’m a big fan of the simplicity that Ant brings to the mix, but the issue of dependency management being baked into Maven makes it extremely appealing; I do like Ivy as an alternative when using Ant, but I wasn’t going to re-write the company’s build process. So we had flex-mojos building our source and producing artifacts for deployment to our team’s Maven repository, but we needed to integrate our unit tests into our build. We were using dpuint and were excited to see that fluint had been released with Ant support. Currently flex-mojos doesn’t support Fluint, although my colleagues tell me they’re working on it, so I knew I was going to have to use Ant. To start I had to get flex-mojos building my test SWF so I could use the Fluint Ant task. The Ant support in Fluint requires that you produce a module SWF that will work with their test runner written in AIR. After an hour of messing with flex-mojos, I was unable to get the compile, or test-compile, goal to do what I wanted, so I decided to use the maven-ant-run plugin to compile our tests as well.

Below is the snippet I was able to get working to compile our tests and execute the Fluint test runner:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<properties>
   <flex.home>PATH_TO_FLEX_SDK_HOME</flex.home>
   <fluint.testrunner>PATH_TO_FLUINT_AIR_RUNNER_EXECUTABLE</fluint.testrunner>
</properties>
...
<build>
   <plugins>
      <plugin>
         <groupId>info.flex-mojos</groupId>
         <artifactId>flex-compiler-mojo</artifactId>
         <version>2.0M9</version>
         <extensions>true</extensions>
         <configuration>
            <skipTests>true</skipTests>
         </configuration>
      </plugin>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-antrun-plugin</artifactId>
         <executions>
            <execution>
               <id>fluint-test-compile</id>
               <phase>test-compile</phase>
               <configuration>
                  <tasks>
                     <!-- Pull in Flex Ant Tasks -->
                     <taskdef resource="flexTasks.tasks" />
 
                     <property name="FLEX_HOME" location="${flex.home}" />
 
                     <!-- Create test-classes directory -->
                     <mkdir dir="${project.build.testOutputDirectory}" />
 
                     <mxmlc file="${project.build.testSourceDirectory}/AirRunner.mxml" 
                        output="${project.build.testOutputDirectory}/AirRunner.swf" 
                        keep-generated-actionscript="false">
 
                        <load-config filename="${FLEX_HOME}/frameworks/flex-config.xml" />
                        <source-path path-element="${FLEX_HOME}/frameworks"  />
                        <compiler.library-path dir="${FLEX_HOME}/frameworks" append="true">
                           <include name="libs" />
                        </compiler.library-path>
                        <compiler.library-path dir="${project.build.directory}/.." append="true">
                           <include name="libs" />
                        </compiler.library-path>
                        <compiler.library-path dir="${project.build.directory}" append="true">
                           <include name="*.swc" />
                        </compiler.library-path>
                     </mxmlc>      
                  </tasks>
               </configuration>
               <goals>
                  <goal>run</goal>
               </goals>
            </execution>
            <execution>
               <id>fluint-test-run</id>
               <phase>test</phase>
               <configuration>
                  <tasks>
                     <!-- Pull in Fluint Ant Task -->
                     <taskdef name="fluint" classname="net.digitalprimates.ant.tasks.fluint.Fluint" />     
 
                     <property name="test.report.loc" location="${project.build.directory}/surefire-reports" />
 
                     <!-- Create reporting directory -->
                     <mkdir dir="${test.report.loc}" />
 
                     <fluint debug="true" 
                        headless="true"
                        testRunner="${fluint.testrunner}" 
                        outputDir="${test.report.loc}" 
                        workingDir="${project.build.testOutputDirectory}">
 
                        <fileset dir="${project.build.testOutputDirectory}">
                           <include name="**/AirRunner.swf"/>
                        </fileset>
                     </fluint>      
                  </tasks>
               </configuration>
               <goals>
                  <goal>run</goal>
               </goals>
            </execution>
         </executions>
         <dependencies>
            <dependency>
               <groupId>org.apache.ant</groupId>
               <artifactId>ant</artifactId>
               <version>1.7.0</version>
            </dependency>
            <dependency>
               <groupId>flex.ant</groupId>
               <artifactId>flexTasks</artifactId>
               <version>1.0.0</version>
            </dependency>
            <dependency>
               <groupId>net.digitalprimates</groupId>
               <artifactId>FluintAnt</artifactId>
               <version>1.0.1-SNAPSHOT</version>
            </dependency>
         </dependencies>
      </plugin>
   </plugins>
</build>

Couple of things to point out about the above snippet:

  • There are two dependencies on resources being available on the disk, the Flex SDK (flex.home property) and the Fluint Air Runner (fluint.testrunner property).
  • Using Ant I had to create the “test-classes” and “surefire-report” directory to stick with Maven conventions.
  • We adhered to the convention of naming our test runners for the Fluint Ant task “AirRunner.mxml” so we could use this snippet in a parent POM.
  • I had to change the dependency for the maven-ant-run plugin from Ant 1.6.5, which is the default, to Ant 1.7.0, which is required by the Fluint Ant task.

You may also notice that I’m using snapshot versions of the Fluint library and the Fluint Ant task. I ended up having to change the source of the Fluint library, Ant task, and AIR runner to get Fluint to work as I wanted it to with my build. Fluint is an awesome unit testing library, it just needed some tweaks. I made changes to fix the following:

  • The XML output from the Fluint AIR runner wasn’t compliant with what the Surefire Report plugin was expecting.
  • The name of output file from the Fluint AIR runner was in the convention “TEST-*.xml” which the SureFire reporting plugin expects.
  • Fluint had the notion of an error and failure being separate but it wasn’t implemented for the Flash or AIR test runners.
  • The Ant task didn’t allow the user to specify a working directory so that the AIR runner could be launched from the appropriate directory.

I later found out that AIR and relative paths don’t play nicely together (= at all, unless there is helper code), so we also had to re-factor our test suites to NOT rely on any assets unless they are embedded or referenced with absolute URIs. This made the change to the Fluint Ant task kinda worthless, but I kept it in anyway for when AIR works in the future. Additionally, it’s important to note, that the “headless” mode in the Fluint AIR runner is really just a minimized window that closes after the XML report is written; if you plan on running your CI build on an OS without a windowing solution, then FLuint will not work since AIR does not support running in a true headless mode. On a side note, my changes should address issues #5 and #22 on the Fluint Google Code site; issue #21 should be solved by the Ant dependency fix I spoke about above and issue #20 is just a matter of the fluintAnt15.jar being compiled with Java 1.6 instead of 1.5, I believe. I’ve submitted these fixes along with my code to the Fluint guys in an email, just haven’t heard anything back yet.

So at this point I had the build process working as I wanted such that I could run “mvn clean deploy site” and find a snapshot in our team’s maven repository and site documentation generated. On to CI. I have used CruiseControl many times in the past, but the idea of being entrenched in XML, especially with all the Maven and Ant fun, was discouraging so I decided to give Hudson a try this time around. Wow … Hudson is amazing improvement over CruiseControl. Completely UI driven, I have yet to find myself digging through XML and best of all. The post-build support feel a little lighter than CruiseControl’s, but I think that’s just because I haven’t come across an X10 plugin so we can get a stop light or glowing orb setup. Hudson provides trend reporting on builds and unit tests as well as embedded reporting for unit tests and xdoclet-like documentation; it also has tons of Groovy integration which I really like (not that we’re using it … yet). Initially I chose to go with the pure Maven build for our projects, but I then decided to switch back to the free-style build; I couldn’t get trend reporting for unit tests to work with the pure Maven build, so I think my conventions are off for the Fluint reporting. In the free-style build I set the “site” directory as the Javadoc location and the “surefire-report” directory as the test report directory. Even though there is more configuration in a free-style build, it was simpler in the short run to get what I wanted in Hudson running. If unit test trend reporting isn’t as important to your CI needs, then the pure maven build may be more along the lines of what you’d like to use so that you can get the additional build trigger “Build whenever a SNAPSHOT dependency is built”. On a side note, I’m working towards using the FlexCover support in flex-mojos to make our site reports complete, but haven’t had a chance to dive in yet.

I hope some of the hurdles I encountered can help if you’re trying to get your CI process working with Flex. The flex-mojos, Fluint, and Hudson guys have some good walkthroughs/tutorials to cover the details I left out. I’m always up for suggestions, so definitely feel free to rip into my solutions. :)

Adogo October Meeting Tomorrow – Build Tool Shoot Out

Brian LeGros | October 2nd, 2007 | news  

Just in case anyone had forgotten, the Adogo (http://adogo.us) meeting for October is tomorrow. We’re going to be having the “Build Tool Shoot Out” to continue our series on “Ideas on Software Configuration Management”. I’ll be presenting on Ant and I’ve just finished putting some finishing touches on my presentation; I’m hoping it’ll turn out well. I’m really excited to see Max, Daniel, and Tyler’s presentations. Ant has always been the defacto build tool for Adobe folks, so I’m hoping that the group can walk away with quite a few options to consider when thinking about building.

I’m also hoping that quite a few folks from the ORUG will make it out to our meeting tomorrow night since 2 Ruby build tools are being represented (Rake and Buildr). We didn’t publicize this meeting with the other UG’s that much, which was probably a mistake on our part, but I hope a few stragglers find their way over to the meeting.

No word on sponsors yet, so we may be doing cookies, but come on out and support the Adogo in what promises to be a great meeting. If anything, come out to see me forgot tons of details on Ant so that you can publicly “boo” and “hiss” my presentation. See you there!