<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2139974165898401757</id><updated>2011-11-29T21:02:42.578-08:00</updated><category term='ruby'/><category term='datasets'/><category term='AES'/><category term='date parsing'/><category term='digest'/><category term='rails rails2.0 update-problems actionmailer rubyforge gems'/><category term='google maps kml ruby util geo'/><category term='flex framework mvc'/><category term='openid rails flex login'/><category term='fedora'/><category term='gorm'/><category term='rpx'/><category term='temporal'/><category term='jdk-7'/><category term='RSA'/><category term='jsr-310'/><category term='git backup database sql'/><category term='haxe flex adobe'/><category term='grails'/><category term='mac osx'/><category term='encryption'/><category term='Bouncy Castle'/><category term='database schema rails migration'/><category term='excel'/><category term='flex adobe git'/><category term='active record'/><category term='grails groovy gorm java'/><category term='jetty'/><category term='migrations'/><category term='rails'/><category term='rails flex remoting rest flex2rails'/><category term='uml'/><category term='test driven development'/><category term='common login'/><category term='java groovy'/><category term='closures'/><category term='grails groovy gorm fixtures rails java closures'/><category term='xml'/><category term='hibernate'/><category term='fixtures'/><category term='spread sheet'/><category term='reports'/><category term='soap'/><category term='mysql'/><category term='java'/><category term='java jsr-310'/><category term='jpeg'/><category term='flex HTML5 CSS3 W3C'/><category term='programming practices'/><category term='git subversion'/><category term='amazon s3'/><category term='screen capture'/><category term='rails flex sprouts'/><category term='flex'/><category term='resin'/><category term='netbeans'/><category term='seo'/><category term='rest'/><category term='virtual servers'/><category term='jxl'/><category term='adobe flex'/><category term='groovy'/><category term='flex 4'/><category term='database design'/><category term='flash builder'/><category term='flex4'/><category term='javax.time'/><category term='slicehost'/><category term='file upload'/><category term='unit testing'/><category term='bit.ly'/><category term='datetime'/><category term='software architect'/><category term='netbeans 6.9'/><category term='flex unit'/><category term='crypto'/><title type='text'>rain city digest</title><subtitle type='html'>thoughts on open source groovy, ruby, grails, rails, java, and adobe flex, linux, osx...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>52</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-244491991765411784</id><published>2010-09-23T08:04:00.000-07:00</published><updated>2010-09-23T08:25:44.549-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='netbeans 6.9'/><category scheme='http://www.blogger.com/atom/ns#' term='mac osx'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>Easy Mac Installs--MySQL &amp; NetBeans</title><content type='html'>I just went through two very easy installs on my new mac; the first, MySQL 5.1.5.  A few years ago, when I installed on my old mac it was the typical tar ball install that linux users are familiar with.  Not bad, especially if you have a canned script to remind you of all the steps.   &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But now there are two dmg package files that do most of the work for you.  And, it installs in /usr/local with a dynamic link to mysql--just like it's supposed to be.  The only steps left were to remove the anonymous users, supply the root and admin passwords and reboot to start the service.  Easy.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Next was NetBeans 6.9.  I have been using 6.8 for a few projects, and have loved the support for groovy and especially grails, but I have held off upgrading while in full development mode.  But now, with a new grails project in it's infant stages, I decided to make the move.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Installation was easy.  I did have to manually replace the 6.8 item with 6.9 on my doc, but other than that all went easy.  It found my current projects and I was able to run unit tests from within the IDE.  Once again, easy.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Happy coding...&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-244491991765411784?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/244491991765411784/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=244491991765411784' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/244491991765411784'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/244491991765411784'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2010/09/easy-mac-installs-mysql-netbeans.html' title='Easy Mac Installs--MySQL &amp; NetBeans'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-7987750577378269600</id><published>2010-09-22T20:50:00.000-07:00</published><updated>2010-09-23T08:28:32.754-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='netbeans'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Grails 1.3.4, Groovy 7 and NetBeans 6.8</title><content type='html'>After about a year of doing nothing but Adobe Action Script projects, I'm finally back to a java/groovy/grails project.  So, I downloaded the latest grails and fired up my version of NetBeans (6.8) and generated some code.  Here's what I discovered...&lt;br /&gt;&lt;br /&gt;First, NetBeans is very groovy/grails friendly.  I was able to generate the project inside the IDE.  I then dropped to the command line to generate domain classes and tests.  I was able to run the full test suite inside the IDE, but I still prefer the command line.&lt;br /&gt;&lt;br /&gt;I noticed that the generated code doesn't include an ant script, but just as well.  I'm so sick of the XML crap, and the grails command line does everything you need, so...&lt;br /&gt;&lt;br /&gt;Next step is to generate controllers, and maybe a few views, but I usually use grails as a JSON or XML server with a Flex/Action Script UI.   I'm also creating all the unit tests as I go and running them from inside the IDE and from the command line.&lt;br /&gt;&lt;br /&gt;Next post will discuss the controllers and any problems related to getting the application into production.  So far everything works as advertised--better than previous versions, and very productive.  My new best friend...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-7987750577378269600?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/7987750577378269600/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=7987750577378269600' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7987750577378269600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7987750577378269600'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2010/09/grails-134-groovy-7-and-netbeans-68.html' title='Grails 1.3.4, Groovy 7 and NetBeans 6.8'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-3487807987823767788</id><published>2010-08-10T18:56:00.000-07:00</published><updated>2010-08-10T19:05:06.379-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='uml'/><category scheme='http://www.blogger.com/atom/ns#' term='software architect'/><title type='text'>UMLet UML Diagraming</title><content type='html'>Just had to comment on a very useful new(ish) project for editing UML documents called &lt;a href="http://www.umlet.com/"&gt;UMLet&lt;/a&gt;.  No frills or bs* just a good productivity tool in the open source space.  Human readable file structure to enable old timers like me to hack in attributes with vi.&lt;br /&gt;&lt;br /&gt;Converts your drawings to png, jpg, eps, pdf, svg, etc from inside the UI or even from the command line (a real programmer's tool).&lt;br /&gt;&lt;br /&gt;It comes in two flavors 1) eclipse plugin, and 2) standalone Swing application.  They have a few skins that help ease the swing pain but who cares about the L&amp;amp;F, I just want a tool that works with me instead of against me.&lt;br /&gt;&lt;br /&gt;So, thanks to the UMLet team for a great product!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-3487807987823767788?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/3487807987823767788/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=3487807987823767788' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3487807987823767788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3487807987823767788'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2010/08/umlet-uml-diagraming.html' title='UMLet UML Diagraming'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-4784835438993915013</id><published>2010-04-16T00:43:00.000-07:00</published><updated>2010-04-17T08:28:10.338-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flex HTML5 CSS3 W3C'/><title type='text'>HTML5, CSS3, Apple, Google and the W3C</title><content type='html'>About a year ago, I remember responding to a question from a colleague concerning Flex in comparison to HTML5 by saying that HTML5 was not ready yet.  For the most part, I still hold that opinion but with additional qualifications. &lt;br /&gt;&lt;br /&gt;We are currently bidding on a job that requires iPad as the target device and another that needs a private label application that will work on the desktop,  iPhone and eventually Android and Blackberry.   After logging many hours researching objective-c and HTML5/CSS3 my first thoughts were that all development needed to be done in objective-c.  But, after viewing some of the other videos from Apple, MIT, and Stanford, and doing some small tests, I think that most applications for the iPad/iPhone can be implemented completely in HTML5/CSS3.  So here is my revised/updated response...&lt;br /&gt;&lt;br /&gt;If you have complete control over the target web browser (i.e., webkit/khtml based) then HTML5 and CSS3 can't be beat.  Using the 'canvas' tag/object enables drawing anything that you can draw with flex/flash and also includes drawing text (not possible with flex).  Using CSS3 enables all the transitions that are currently available in flex including easing methods.  Using WebSocket, provides peer-to-peer real time communications.   So if your target includes Safari, iPhone, iPad, iPod/Touch, Google Chrome, Opera, Firefox, as well as the Android, Sprint and Blackberry browsers, the new technology is ready today.&lt;br /&gt;&lt;br /&gt;The missing piece is IE, currently at 70% of desktop browser installations.  An application that needs to run only in a standard client browser is still better off using Flex (95% penetration) until Microsoft complies with the latest W3C specs.  But, the flash player will probably never make it to small devices (it's a hog), so if the requirement is to run on desktop and phone, especially with touch gestures, HTML5/CSS3 is probably a better choice.  The down side is waiting for Microsoft to catch up with the fully developed world and hoping they will fully comply with the W3C.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-4784835438993915013?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/4784835438993915013/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=4784835438993915013' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/4784835438993915013'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/4784835438993915013'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2010/04/html5-css3-apple-google-and-w3c.html' title='HTML5, CSS3, Apple, Google and the W3C'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-2025520336561572137</id><published>2010-02-11T21:01:00.000-08:00</published><updated>2010-02-11T22:01:20.214-08:00</updated><title type='text'>Action Script Threads for Report Generation</title><content type='html'>My requirement:  to create a complex report that may take several seconds to run without blocking the user's interaction with the application.  In java the solution is simple: create a thread and invoke start().  In action script, it's a bit more difficult.&lt;br /&gt;&lt;br /&gt;Action Script doesn't support threads directly out the the box but it is run in a multi-threaded framework, so I knew there must be a solution.   I took some time to research how other flex developers were implementing Action Script threads and concluded that the best solution was to simply use the Timer class to create a new threads.&lt;br /&gt;&lt;br /&gt;The timer class has an asynchronous event called TIMER_COMPLETE that is perfect for creating a bloated, heavy weight thread.  But, if all you need is a separate thread to run while creating a huge report, this does the trick.&lt;br /&gt;&lt;br /&gt;One of the practical considerations is that you will probably want a supporting class to encapsulate run parameters for each thread you create.  For reports this works well because there is enough logic in each individual report to warrant its own class.  And as long as the report class implements the timer start method then you are in good shape.&lt;br /&gt;&lt;br /&gt;The simplest implementation looks like this:&lt;pre  style="padding-left: 10px;font-size:12px;"&gt;&lt;br /&gt;public function start():void {&lt;br /&gt;var timer:Timer = new Timer(1, 1);&lt;br /&gt;timer.addEventListener(TimerEvent.TIMER_COMPLETE, &lt;span style="color: rgb(255, 102, 102);"&gt;run&lt;/span&gt;, false 0, true);&lt;br /&gt;timer.start();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// override this base class to implement the run worker&lt;br /&gt;protected function &lt;span style="color: rgb(255, 102, 102);"&gt;run&lt;/span&gt;():void {&lt;br /&gt;// create the report or whatever you need...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Implement the run handler and you are set.  The class can encapsulate any run time parameters that you require including a common dispatcher to dispatch a 'COMPLETE' event when the process is finished.  Easy.&lt;br /&gt;&lt;br /&gt;This is an acceptable work-around for a language that does not support threads right out of the box.  But for the rare occasion that you really need a thread to enable interaction while a heavy-weight, time consuming task is running, this really does the trick.&lt;br /&gt;&lt;br /&gt;Happy coding...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-2025520336561572137?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/2025520336561572137/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=2025520336561572137' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2025520336561572137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2025520336561572137'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2010/02/action-script-threads-for-report.html' title='Action Script Threads for Report Generation'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-3132669193021233110</id><published>2010-01-24T22:09:00.000-08:00</published><updated>2010-01-24T22:09:00.292-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming practices'/><title type='text'>Transforming Average Programmers into Power Developers</title><content type='html'>The term &lt;a style="font-style: italic;" href="http://en.wikipedia.org/wiki/Software_crisis"&gt;software crisis&lt;/a&gt; coined in the late 60s described the ratio of software required by industry to the inability of software engineers to deliver correct, understandable, and verifiable programs.  Back then the problem was blamed on the relative high power of new computers and the general low quality of programmers.&lt;br /&gt;&lt;br /&gt;And now 40 years on, our industry continues to suffer the &lt;span style="font-style: italic;"&gt;software crisis&lt;/span&gt;, but not because computing power has outstripped programmer ability.   And our crisis cannot be blamed on a lack of programmers; the world has provided an over abundance of programmers, so that's not an issue.    But, as an industry, we still fail to deliver on a consistent basis.    Here are some tips designed to help alleviate our crisis and improve the quality of our programming community.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Learn UNIX.  Ok, linux, mac osx, or cygwin; the main idea is to use the command line in a well thought out shell.  Good programmers program all the time, just like musicians practice scales.  Every time you enter a series of commands  into the command line you are excising your skills.  When you learn the power of command pipelines to solve basic problems your are flexing your ability to solve problems pragmatically.   Learn and use tar, jar, grep, tail, find, sed, awk, lex, yacc, etc.  Use curl, svn, git, ant, make, ssh and scp.   Find a good shell like zsh, bash, ksh or whatever and write scripts to help you organize your work.  Write aliases that tame the command line.  Write sed or egrep scripts to exercise your Regular Expression patterns. The important thing is that "&lt;span style="font-weight: bold; font-style: italic;"&gt;Programmers program--always&lt;/span&gt;".&lt;/li&gt;&lt;li&gt;Expose yourself to as many languages as you can.  You probably have one or two primary languages that you use on the job.  Don't limit yourself to just those.  Create a sandbox using subversion or git and create scripts in ruby, php, perl, javascript and python.  Create java, action script, c++ sandboxes with either ant, gant, rake, or make files to build small applications.  Learn new, or newly recognized languages like Scala, OCaml, Erlang, Haskell and Go.&lt;/li&gt;&lt;li&gt;Contribute to an open source project.  There are a gazillion open source projects on the market now, most of them throw away.  Rather than creating your own, join one that has a chance of succeeding.  Read the project's standards and rules and begin by submitting documentation updates.   Search the bug list and submit proposed fixes, complete with unit tests (don't bother with projects that ignore tested designs).  After a while, if you pass the initial stages, you will become a committer--your right of passage to the open source community.&lt;/li&gt;&lt;li&gt;Read and re-read the APIs.  The programming language that you primarily work in was created by several brilliant people that have devoted thousands of hours to create a useful platform.  Don't ignore their work.  Even if the platform solution may not exactly fit your situation, at a minimum, know what it does first before creating your own.  Dive deep and read the platform source code.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Read the expert blogs.  Posts to Object Mentor, InfoQ, Artima and experts like Martin Fowler, Ward Cunningham, Dave Thomas, Kent Beck, and a host of others.  Find the blogs that are specific to your language.  Look for web or pod casts.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Keep up with technology changes.  IoC from Spring Source or Google, Agile and Extreme Programming, Rails Active Record, Hibernate, Apect Oriented Objects, JUnit 4, Flex Unit 4, DSLs, Patterns, Idioms.  Lots to keep up with, some hype, others real.&lt;/li&gt;&lt;li&gt;Write readable, clean code.  The definition of clean code is 1) code that is understandable by someone other than the original author, 2) package, class, and method names that are accurate and fully describe their intended purpose, and 3) code that is fully tested with a suite of automated test tools.  Always choose readability over writability.  As Knuth says, &lt;span style="font-style: italic;"&gt;"A program is essentially a work of literature".&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Read.  Most programmers are in the habit of purchasing lots of technical books.  That's great, but don't let them sit on the shelf.  Read and re-read them.  Dog-ear or sticky note sections that you want to re-visit.  Highlight important points that resonate with you.  And, don't just read the cook-book tutorials.  Your library should contain a healthy mix of literature from publishers like Prentice Hall and Addison Wesley to offset your APress, O'reilly, Manning, and Wrox vocational studies.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;I'm sure that there are more items, but these eight represent a good start. Happy coding!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-3132669193021233110?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/3132669193021233110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=3132669193021233110' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3132669193021233110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3132669193021233110'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2010/01/transforming-average-programmers-into.html' title='Transforming Average Programmers into Power Developers'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-6085763441564066274</id><published>2010-01-10T20:48:00.000-08:00</published><updated>2010-01-10T21:40:19.533-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flash builder'/><category scheme='http://www.blogger.com/atom/ns#' term='closures'/><category scheme='http://www.blogger.com/atom/ns#' term='test driven development'/><category scheme='http://www.blogger.com/atom/ns#' term='flex unit'/><category scheme='http://www.blogger.com/atom/ns#' term='unit testing'/><category scheme='http://www.blogger.com/atom/ns#' term='flex 4'/><title type='text'>Maximizing FlexUnit4 with Mocks and Closures</title><content type='html'>After spending the past few months using FlexUnit4 that comes with the new version of Adobe Flex called Flash Builder, I have a few tips on how to maximized your testing using mocks and closures.   This comes as second nature for java/groovy/ruby programmers, but may be new to some in the Flex/Action Script community, so here goes.&lt;br /&gt;&lt;br /&gt;Mocks: The project that I am currently on uses Swiz for IoC wiring and configuration of controllers, delegates, components, services and loggers.  But, it's not good practice use IoC for tests, so we use mocks.  Mocks that configure themselves, expose protected members, and reassign closures to methods that modify behavior for test purposes.&lt;br /&gt;&lt;br /&gt;Example: The Shape factory: We have a factory class that generates various shapes such as lines, rectangles, ellipses, comment boxes, polylines, curves, etc.  The method closure that creates specific shapes is called createShapeFromType.  In the class constructor this closure is mapped to the factory's local private method createShape that creates and returns a shape object for the specified type.  During some tests, this closure is mapped to createMockShape which returns a mock shape that can be tested independent of the factory.  Other tests still use the mock factory but with the default createShape method in place to test and verify that it's behaving as expected.  Think of it like a second level of polymorphism without having to create complex and ridged hierarchies (class bloat reduction).&lt;br /&gt;&lt;br /&gt;MockShapeFactory: This mock extends ShapeFactory to inherit all of it's normal behaviour.  So when I write a unit test against one of it's methods, I know that I'm exercising the real thing.  But the mock allows me to keep track of what's going on inside the class as the tests run.  And, the mock does it's own configuration mimicking what  IoC performs in production.&lt;br /&gt;&lt;br /&gt;MockShape: This mock has two primary purposes; 1) to configure itself for use, logs, delegates, etc, and 2) to expose protected methods and variables. For example, the shape has mouse event listeners that can be invoked through the test to see if the object moves to a specific set of coordinates.  Or if it's a non-scaling shape, you can verify that the target shape performed a move an resize rather than a simple scaleX/scaleY operation.  You can also test that a vetoed request for focus does indeed ignore the request and remain unfocused. There are many possibilities.&lt;br /&gt;&lt;br /&gt;Some of this is very basic for test junkies, but as I said earlier, the Flex community seems to be just catching on to test driven development.  And, with the new Flex/FlashBuilder and FlexUnit4 there is no excuse to ignore testing any longer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-6085763441564066274?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/6085763441564066274/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=6085763441564066274' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/6085763441564066274'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/6085763441564066274'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2010/01/maximizing-flexunit4-with-mocks-and.html' title='Maximizing FlexUnit4 with Mocks and Closures'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-7356712714813592502</id><published>2009-10-26T12:31:00.000-07:00</published><updated>2009-10-26T12:40:57.610-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jpeg'/><category scheme='http://www.blogger.com/atom/ns#' term='flex 4'/><category scheme='http://www.blogger.com/atom/ns#' term='screen capture'/><title type='text'>Flex 4 BitmapImage</title><content type='html'>Flex 4, and specifically the new spark framework has a BitmapImage object in the spark.primitives package.  It's associated mxml tag is &lt;s:bitmapimage&gt;.  I found this after trying to add a Bitmap object to a VGroup only to get errors.  So, after searching a bit, I found the BitmapImage and simply set the source property to my BitmapData.  Very easy.&lt;br /&gt;&lt;br /&gt;My code problem was to read a remote image then scale it to as close to a target width &amp;amp; height as possible without changing the original aspect ratio.  From there, the user can rescale the image and drag a focus rectangle over the image to select the desired image section.  Then, with the click of a button, the image is scaled and cropped to the optimum size.  I used JPEGEncoder to write the data back out to a new image file.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-7356712714813592502?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/7356712714813592502/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=7356712714813592502' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7356712714813592502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7356712714813592502'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/10/flex-4-bitmapimage.html' title='Flex 4 BitmapImage'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-8719756985119554671</id><published>2009-10-20T17:09:00.000-07:00</published><updated>2009-10-20T17:23:35.581-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flex4'/><category scheme='http://www.blogger.com/atom/ns#' term='adobe flex'/><title type='text'>Flex 4/Flash Builder Beta</title><content type='html'>Adobe released Flash 4 Beta 2 a few weeks ago with many changes and improvements over Flex 3.  The new lightweight spark framework has many advantages over the older mx components and separates skinning in an attempt to facility and advocate better separation of concerns and improved maintainability.&lt;br /&gt;&lt;br /&gt;There are also some significant improvements to the Flash Builder IDE that take much better advantage of the eclipse framework.  It was always a big mystery to my why Flex 2 and 3 Builders seemed to strip so many of the basic features out of eclipse when they created their IDE.  Many of those features, although not all, are now included.&lt;br /&gt;&lt;br /&gt;One basic but vital feature is the ability to create your own standard templates for AS3 classes and MXML applications and components.   The approach follows the eclipse use of internal variables, like ${user}, ${date}, ${year}, etc. &lt;br /&gt;&lt;br /&gt;Another feature is conditional breakpoints and watchpoints.  There is also a way to do remote debugging.&lt;br /&gt;&lt;br /&gt;So it looks like the new Flash Builder comes closer to providing the basic eclipse functionality that has been missing since Flex 1.x was introduced.   I'll post later with other features that have been included while I work my way through the latest Max 2009 presentations and the on-line "Flex 4 in a week" training series.  And, in subsequent posts, I will share what is offered by the new Spark platform.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-8719756985119554671?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/8719756985119554671/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=8719756985119554671' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/8719756985119554671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/8719756985119554671'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/10/flex-4flash-builder-beta.html' title='Flex 4/Flash Builder Beta'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-2388637380204015077</id><published>2009-09-04T18:23:00.000-07:00</published><updated>2009-09-04T19:51:30.030-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='seo'/><category scheme='http://www.blogger.com/atom/ns#' term='bit.ly'/><category scheme='http://www.blogger.com/atom/ns#' term='adobe flex'/><title type='text'>Hotlinks External Page Viewer</title><content type='html'>I first heard about twitter in 2007 while listening to a Ruby On Rails podcast.  It took a while, but I finally got on board, mainly as a result of the mandatory SEO stuff required for my current project.  So, I'm now all a twitter.&lt;br /&gt;&lt;br /&gt;Twitter's 140 character limit leads to a mandatory link for almost every post.   Some links are huge, but with the help of tinyurl.com or bit.ly you can hash down your URL to a very manageable size.   At the same time, the link reduction sites allow you to track the number of hits the link has received.&lt;br /&gt;&lt;br /&gt;There are a couple of companies that have mashed up the URL reducers with the ability to display a personalized reference at the top of the target page.  So, If I tweet about Hillary Duff's new utube video I would be able to display a header that includes a link to my site at the top of the page.  Rather than use one of the off the shelf products, I decided to build my own, mainly because 1) it was trivial to implement, and 2) I (and my customer) wanted custom graphics displayed on the banner without the third party interference.&lt;br /&gt;&lt;br /&gt;So, here is a sample of what I mean.  The original tweet would look a bit like this: "check out Hillary Duff's new utube thingy here: &lt;a href="http://bit.ly/30YbdB"&gt;http://bit.ly/30YbdB&lt;/a&gt;".   When the tweet recipient clicks on the link, they see the video as described, but they also see the Rain City Software banner at the top of the page, complete with links to my web site and twitter account.  Go ahead, &lt;a href="http://bit.ly/30YbdB"&gt;give it a try&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_7lr-p1KyMn0/SqHDDU4nN1I/AAAAAAAAAIo/VPxuy1159H8/s1600-h/hotlinks-result-page.jpg"&gt;&lt;img style="cursor: pointer; width: 400px; height: 139px;" src="http://4.bp.blogspot.com/_7lr-p1KyMn0/SqHDDU4nN1I/AAAAAAAAAIo/VPxuy1159H8/s400/hotlinks-result-page.jpg" alt="" id="BLOGGER_PHOTO_ID_5377793891970266962" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As it turns out, this is very simple to do with a simple iframe to display the target URL.  The banner is small and hopefully not offensive and can be closed by clicking on the 'X' icon.  Or, the user can click on the logo to get to the Rain City site or click on the tweety birds to get to Rain City's twitter account.  All sorts of possibilities (including incorporating swf's, but lets keep it simple for now).  And, the clicks are tracked not only by bit.ly but for google analytics as well.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Creating the Hotlink:&lt;/span&gt;  I made the hotlink creation easy with a small web based UI.  The user simply enters a title and pastes in the link then clicks create (after selecting the correct server).  The hotlink is created and can be copied to bit.ly, tinyurl or your favorite URL shortener.  Here is what we get for the Hillary Duff link:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7lr-p1KyMn0/SqHDRRFOXtI/AAAAAAAAAIw/X6_1EbmWoW0/s1600-h/hotlinks-creator.jpg"&gt;&lt;img style="cursor: pointer; width: 400px; height: 102px;" src="http://3.bp.blogspot.com/_7lr-p1KyMn0/SqHDRRFOXtI/AAAAAAAAAIw/X6_1EbmWoW0/s400/hotlinks-creator.jpg" alt="" id="BLOGGER_PHOTO_ID_5377794131467591378" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The user can test the link by clicking 'Test Link' to see if all is in order.  If this looks ok, then copy the link to the shortener, make the tweet and that's it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Adobe/Flex UI Code&lt;/span&gt;:  As you can see from the code below, the code is quite simple.  Click handlers for create and test, and the use of escape to get the title and target URL (hotlink) to work correctly as http parameters.  I thought about using he open APIs provided by many URL shorteners, but decided to just let the user copy/paste the target URL.  Here is the Flex xml code:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_7lr-p1KyMn0/SqHDb6p6D4I/AAAAAAAAAI4/2Sipji-t-iw/s1600-h/hotlinks-flex-code.jpg"&gt;&lt;img style="cursor: pointer; width: 389px; height: 400px;" src="http://1.bp.blogspot.com/_7lr-p1KyMn0/SqHDb6p6D4I/AAAAAAAAAI4/2Sipji-t-iw/s400/hotlinks-flex-code.jpg" alt="" id="BLOGGER_PHOTO_ID_5377794314425995138" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Hotlink Service Code&lt;/span&gt;:   I decided to implement the service code in ruby but php or java would have worked just as well.  The objective is to pull the title and link from the URL parameters and use those inside the standard template code.   The template code should probably be separated out, but it was easy enough to keep everything all in one file.  The banner can be any combination of backgrounds, links, whatever.   Anyway, here is the ruby code:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_7lr-p1KyMn0/SqHDljv-OfI/AAAAAAAAAJA/m9VOkbs34nw/s1600-h/hotlinks-ruby-code.jpg"&gt;&lt;img style="cursor: pointer; width: 353px; height: 400px;" src="http://4.bp.blogspot.com/_7lr-p1KyMn0/SqHDljv-OfI/AAAAAAAAAJA/m9VOkbs34nw/s400/hotlinks-ruby-code.jpg" alt="" id="BLOGGER_PHOTO_ID_5377794480076110322" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So that's it.  Easy to use and all the tracking that an SEO could hope for.  And, very customizable to suite the customer's requirements.   Let me know if you want help setting up your own.  I would be happy to help.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-2388637380204015077?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/2388637380204015077/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=2388637380204015077' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2388637380204015077'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2388637380204015077'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/09/hotlinks-external-page-viewer.html' title='Hotlinks External Page Viewer'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_7lr-p1KyMn0/SqHDDU4nN1I/AAAAAAAAAIo/VPxuy1159H8/s72-c/hotlinks-result-page.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-7181474400067500420</id><published>2009-08-27T13:54:00.000-07:00</published><updated>2009-08-27T15:05:42.418-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='adobe flex'/><category scheme='http://www.blogger.com/atom/ns#' term='rpx'/><category scheme='http://www.blogger.com/atom/ns#' term='common login'/><title type='text'>Social Network Common Login in Flex</title><content type='html'>JanRain provides a free version of their industry-leading OpenID   solution.   &lt;a href="http://rpxnow.com/"&gt;RPX Basic&lt;/a&gt; enables   websites to accelerate user registration and login success by   allowing visitors to easily sign in with one of their existing third   party accounts from AOL, Facebook, Google, MySpace,  Yahoo! and more.&lt;br /&gt;&lt;br /&gt;Their product includes a drop-in widget written in Javascript that does all the communications to their open id server and third party providers.  But, for Flex applications (or Java Applets) they provide a set of API calls that do the job.&lt;br /&gt;&lt;br /&gt;Here is what our custom login dialog looks like:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_7lr-p1KyMn0/Spb1qziZ74I/AAAAAAAAAIA/WCo6C5Gar_8/s1600-h/lgc-login-dialog.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 203px;" src="http://1.bp.blogspot.com/_7lr-p1KyMn0/Spb1qziZ74I/AAAAAAAAAIA/WCo6C5Gar_8/s320/lgc-login-dialog.jpg" alt="" id="BLOGGER_PHOTO_ID_5374753321051090818" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The user simply clicks the preferred provider, and the appropriate API call is made to RPX to open a new window.    For example, if the user clicks the facebook icon, the following page is displayed.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7lr-p1KyMn0/Spb32c5pvYI/AAAAAAAAAII/R378Dhj0D2g/s1600-h/facebook-login.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 198px;" src="http://3.bp.blogspot.com/_7lr-p1KyMn0/Spb32c5pvYI/AAAAAAAAAII/R378Dhj0D2g/s320/facebook-login.jpg" alt="" id="BLOGGER_PHOTO_ID_5374755720156265858" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If the users successfully logs into facebook, a token is returned to the Flex application.   The next step is to use this token to access the user's profile, email, photo, etc.  Here's what the user see's after they log in.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7lr-p1KyMn0/Spb6BnwMsEI/AAAAAAAAAIQ/07S4rEnud9Y/s1600-h/member-profile.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 250px;" src="http://3.bp.blogspot.com/_7lr-p1KyMn0/Spb6BnwMsEI/AAAAAAAAAIQ/07S4rEnud9Y/s320/member-profile.jpg" alt="" id="BLOGGER_PHOTO_ID_5374758111071219778" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The same thing happens if the user logs in with any of the other providers.  Subsequent logins don't require the approval, but simply log in without the intermediate page.  So, if I'm already logged into facebook and have approved access from my Flex application, the click skips the facebook login and returns the token.  Simple.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Code&lt;/span&gt;: The code adheres to the MVC paradigm and is short and sweet.  A single controller talks to the RPX API and a login dialog mxml handles the display and clicks.   The controller publishes two events loginComplete and accessFailed.   The dialog processes the provider click through the controller while listening for the response event.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Configuration&lt;/span&gt;:  When you sign up with JanRain for RPX Basic, there are a few configurations that need to be made to get facebook and other third party authorization platforms to work.  The process is simple and only takes a few minutes. &lt;br /&gt;&lt;br /&gt;Testing was easy for all six of the providers that our application uses.  And, there are more coming on line all the time.   It's a great example of distributed processing that eliminates user resistance to signing up for your latest social network application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-7181474400067500420?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/7181474400067500420/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=7181474400067500420' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7181474400067500420'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7181474400067500420'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/08/social-network-common-login-in-flex.html' title='Social Network Common Login in Flex'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_7lr-p1KyMn0/Spb1qziZ74I/AAAAAAAAAIA/WCo6C5Gar_8/s72-c/lgc-login-dialog.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-1903068618978260633</id><published>2009-07-17T16:25:00.000-07:00</published><updated>2009-07-17T16:36:46.276-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='crypto'/><category scheme='http://www.blogger.com/atom/ns#' term='digest'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Simple Digest to Test File Uniqueness</title><content type='html'>Here is a simple way to use the groovy Digest class from org.raincity.glib.crypto package.  It's limited to reasonably small files, but could be used with chunked sections of files.  In any case, here is the test script:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;void testStringDigest() {&lt;br /&gt;  def algorithm = 'SHA-512'&lt;br /&gt;  def s1 = 'this is test 1'&lt;br /&gt;  def s2 = 'this is test 2'&lt;br /&gt;               &lt;br /&gt;  def hash1 = new Digest().createHashString( s1, algorithm )&lt;br /&gt;  def hash2 = new Digest().createHashString( s2, algorithm )&lt;br /&gt;               &lt;br /&gt;  println "h1 = ${hash1}"&lt;br /&gt;  println "h2 = ${hash2}"&lt;br /&gt;               &lt;br /&gt;  assert hash1 != hash2&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;s1 = the byte stream from the first file, s2 is the byte stream from the second file.  If the hashes match, then the files are identical, i.e., not unique.&lt;br /&gt;&lt;br /&gt;Digest's default algorithm is SHA-256, but SHA-512 is available in java &gt; 1.4 and is much stronger.  The 256 default was designed to match the SHA-256 limit for Adobe/Flex.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Thanks to Brad Rhoads for inspiring this post...&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-1903068618978260633?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/1903068618978260633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=1903068618978260633' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/1903068618978260633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/1903068618978260633'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/07/simple-digest-to-test-file-uniqueness.html' title='Simple Digest to Test File Uniqueness'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-302708767892774500</id><published>2009-06-04T17:53:00.000-07:00</published><updated>2009-06-17T06:14:50.417-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='amazon s3'/><title type='text'>Amazon S3 Access with JetS3t</title><content type='html'>Back in my ruby/rails days, I always accessed Amazon S3 from the command line.  No that I'm coding in groovy/grails, I thought I would search for a groovy solution.  Well, I found one, but it's not ready for prime-time.  But, I did come across JetS3t, written in java with examples in groovy, so I thought I would give it a try.&lt;br /&gt;&lt;br /&gt;After downloading, I opened the README and found that JetS3t comes with a nifty set of stand-alone and web based UI application/applets for accessing, browsing and uploading files.  The application is called Cockpit and I was able to easily use it inside Firefox and as a standalone Java application.&lt;br /&gt;&lt;br /&gt;Find out more about &lt;a href="http://jets3t.s3.amazonaws.com/index.html"&gt;JetS3t suite of applications&lt;/a&gt; and be sure to read &lt;a href="http://www.ibm.com/developerworks/java/library/j-s3/index.html"&gt;Andrew Glover's tutorial.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-302708767892774500?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/302708767892774500/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=302708767892774500' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/302708767892774500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/302708767892774500'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/06/amazon-s3-access-with-jets3t.html' title='Amazon S3 Access with JetS3t'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-5195838778513192709</id><published>2009-04-22T20:44:00.000-07:00</published><updated>2009-04-22T20:52:01.351-07:00</updated><title type='text'>Lame Flex IDE</title><content type='html'>Ok, Adobe Flex, or AS3 is an OK language.  But their commercial Flex IDE sucks.  Adobe uses (abuses) the eclipse framework for their commercial IDE.  They charge big bucks for a somewhat visual editor that amounts to a bucket of shit.&lt;br /&gt;&lt;br /&gt;After wrestling with it for over two years I'm tempted to go back to vi and the command line.  Anyone else feel this way?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-5195838778513192709?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/5195838778513192709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=5195838778513192709' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/5195838778513192709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/5195838778513192709'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/04/lame-flex-ide.html' title='Lame Flex IDE'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-5439183906724443159</id><published>2009-03-23T14:30:00.000-07:00</published><updated>2009-03-23T14:30:09.599-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='date parsing'/><category scheme='http://www.blogger.com/atom/ns#' term='adobe flex'/><title type='text'>Parsing ISO 8601 Dates in Flex</title><content type='html'>Flex has many capabilities, but parsing dates is not one of them.  As a member of the &lt;a href="https://jsr-310.dev.java.net/"&gt;JSR-310&lt;/a&gt; team I am focused on date formatting and parsing across many formats, so it comes as a bit of a surprise that Flex has very little support for this.  I guess it's time to roll my own.&lt;br /&gt;&lt;br /&gt;Unlike Java date formatters, Flex doesn't let you set the parse format to accept custom inputs.  The formats must match one of the seven standard parse formats that Flex supports. Some third party examples try to use &lt;a href="http://www.igorcosta.com/flex3/doc/index.html?package-summary.html&amp;all-classes.html"&gt;Date.parse()&lt;/a&gt; after massaging the input string, but they are a bit lame.  &lt;br /&gt;&lt;br /&gt;A good way to parse dates in Flex is to use &lt;a href="http://livedocs.adobe.com/flex/3/html/help.html?content=12_Using_Regular_Expressions_03.html"&gt;RegExp&lt;/a&gt; to extract the numbers.  Then, the numbers need to be validated prior to assignment.  First, lets look at a few code snippets to see how to pull the numbers out of a local date (no time zone offset).&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;// ISO 8601 date time as YYYY-MM-DDThh:mm:ss.SSS&lt;br /&gt;var dateTimeArray:Array = inputDate.split('T')&lt;br /&gt;var dateString:String = dateTimeArray[0]&lt;br /&gt;var timeString:String = dateTimeArray[1]&lt;br /&gt;&lt;br /&gt;// parse the date from YYYY-MM-DD&lt;br /&gt;var pattern:RegExp = /(^\d{4})-(\d{2})-(\d{2}$)/&lt;br /&gt;var result:Object = pattern.exec( dateString )&lt;br /&gt;if (result == null)&lt;br /&gt;  throw(new Error("invalid date format"))&lt;br /&gt;&lt;br /&gt;var year:uint = new uint( result[1] )&lt;br /&gt;var month:uint = new uint( result[2] )&lt;br /&gt;var day:uint = new uint( result[3] )&lt;br /&gt;&lt;br /&gt;if (!validateDate(year, month, day))&lt;br /&gt;   throw(new Error("invalid date format"))&lt;br /&gt;&lt;br /&gt;var date:Date = new Date(year, month - 1, day, 0, 0, 0, 0)&lt;br /&gt;&lt;br /&gt;// now parse the time from HH:MM:SS.SSS&lt;br /&gt;pattern = /(^\d{2}):(\d{2}):(\d{2}).(\d{3})/&lt;br /&gt;result = pattern.exec( timeString )&lt;br /&gt;if (result == null)&lt;br /&gt;   throw(new Error("invalid time format"))&lt;br /&gt;&lt;br /&gt;var hour:uint = new uint( result[1] )&lt;br /&gt;var minute:uint = new uint( result[2] )&lt;br /&gt;var second:uint = new uint( result[3] )&lt;br /&gt;var milli:uint = new uint( result[4] )&lt;br /&gt;&lt;br /&gt;if (!validateTime(hour, minute, second, milli))&lt;br /&gt;   throw(new Error("invalid time format"))&lt;br /&gt;&lt;br /&gt;date.setHours(hour, minute, second, milli)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Lenient Dates and Times&lt;/span&gt;:  It appears that in Flex world, March 32nd == April 1st.  And April 2nd at 25 hours == April 3rd at 1am.  Is this an April fools joke?  No, it's just the lenient way that Flex handles date parameters.  No problem with that until you start parsing, then you need to be strict.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Strict Dates&lt;/span&gt;: Lets say a vendor sends an electronic invoice to you with a payment due date of 2009-May-31. Does he want his money on June 1st?  Probably not.  The best response is to reply that his date is invalid and he must submit a valid date.  &lt;br /&gt;&lt;br /&gt;That's what being strict means.  Unfortunately, Flex does not have a way of controlling strict dates, so you need to do this manually.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Validation&lt;/span&gt;: Without validation constraints a Flex date can range from 0100-01-01T00:00:00.000 to beyond the year 10,000. Dates earlier than the year 100 are coerced to 1900. This is clearly, not what we want. For parsing purposes there should be a range of minimum/maximum acceptable dates.  For discussion purposes, I'll set the range to start at the recognized Gregorian start date of 1582-10-17T00:00:00.000 and end at an arbitrary future date of 2999-12-31:23:59:59.999.&lt;br /&gt;&lt;br /&gt;Now that we have a date range, the years are easy, 1582..2999.  Months for Flex follow the old C convention of zero indexing, so months range from 0..11 but we will use 1..12 for validation then decrement the month prior to creating the flex Date object.  &lt;br /&gt;&lt;br /&gt;Days are a bit trickier, but lets use 31 as the default, then 30 for Apr, Jun, Sep, Nov and 28/29 for Feb, after determining the leap year.  Here is the standard calculation for leap year lifted from the white book:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;var leap = ((year % 4 == 0) &amp;&amp; ( !(year % 100 == 0) || (year % 400 == 0)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Hours, minutes, seconds and milliseconds are bound by 0..23, 0..59, 0..59, and 0..999.  This ignores time zone changes and the occasional leap-second, but otherwise works fine.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The Final Code&lt;/span&gt;: I created a demo application that allows you to plug in and parse dates and run the unit tests.  The demo and source code are available &lt;a href="http://www.raincitysoftware.com/flex/DateTimeParser.html"&gt;here&lt;/a&gt;.  I'm still working on a parser for time-zoned dates, but this is a good start.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-5439183906724443159?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/5439183906724443159/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=5439183906724443159' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/5439183906724443159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/5439183906724443159'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/03/parsing-iso-8601-dates-in-flex.html' title='Parsing ISO 8601 Dates in Flex'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-4554289990421047040</id><published>2009-02-28T07:30:00.000-08:00</published><updated>2009-02-28T07:45:15.718-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsr-310'/><category scheme='http://www.blogger.com/atom/ns#' term='jdk-7'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='javax.time'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>JSR-310 javax.time Periods</title><content type='html'>The proposed JSR-310 date/time API comes with many representations of date and time including Instant, Duration, LocalDate, LocalTime, LocalDateTime, OffsetDate, OffsetTime, OffsetDateTime, ZonedDateTime, MonthDay, YearMonth and others.  One of the more interesting classes is Period.  It represents a quantity of time, not fixed to any time in space--just a quantity.  This entry will discuss periods, how they are parsed, and examples of use.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Parsing&lt;/span&gt;: Periods are parsed from string using  formats that conform to the ISO-8601 duration format PnYnMnDTnHnMn.nS.  Variations of this format are parsed to create Period objects.  Parsing is done in PeriodParser, a standalone helper class that is easily accessed through the static method Period.parse().  Here are a few examples:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;assert Period.parse("PT0S") == Period.ZERO&lt;br /&gt;assert Period.parse("P1Y") == Period.years(1)&lt;br /&gt;assert Period.parse("P10Y8M22DT3M") == Period.period(10, 8, 22, 3)&lt;br /&gt;assert Period.parse("PT1M") == Period.minutes(1)&lt;br /&gt;assert Period.parse("P-4Y") == Period.years(-4)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, the parsing scheme is robust and flexible.  The main thing to keep in mind is that the Period object is more of a value container as opposed to a process unit.  So, 60 minutes won't directly translate to 1 hour, and 24 months does not directly equal 2 years.  But this aside, there are many uses for the Period class.&lt;br /&gt;&lt;br /&gt;For example, lets say I want to periodically archive old temp files from a system.  I could set the period to minutes, hours, days, whatever, then based on an arbitrary start time, begin the archive sweep.  In the same moment, I could compute the next archive sweep by adding my pre-defined period object to the current time.&lt;br /&gt;&lt;br /&gt;A more elaborate implementation would be to self-adjust the scheduled periods based on some criteria.  Sticking with the archive sweep, lets say I set the initial period to 2 hours, or better yet, 120 minutes to give a finer resolution.  Then, at the end of the sweep, I tally archived files.  The smaller the number, the longer the period, on a sliding scale, up to 240 minutes.  A large number would decrease the period to zero minutes, or a continual sweep, so I want to make sure that this is the very worst case scenario.  &lt;br /&gt;&lt;br /&gt;The Period class makes this easy to implement and maintain. To keep it simple, lets just use a linear equation. The formula for calculating the number of minutes between sweeps is reduced to period in minutes = mx + b, where b = 240 minutes (our maximum amount), x = the number of files, and m = the slope.  Lets say that the maximum number anticipated required archives were 2 per minute, so after 240 minutes we would have 480 files that needed to be archived.  If that is our worst case then m = -0.5 (delta y divide delta x, or -240 / 480).  So the curve looks like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_7lr-p1KyMn0/SagbqFAqLUI/AAAAAAAAAEM/zmSHxFD13as/s1600-h/graph.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 247px;" src="http://1.bp.blogspot.com/_7lr-p1KyMn0/SagbqFAqLUI/AAAAAAAAAEM/zmSHxFD13as/s320/graph.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5307522570569526594" /&gt;&lt;/a&gt;&lt;br /&gt;Now each time I do an archive sweep, I tally the files and calculate the next sweep interval using:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;maxMinutes = 240&lt;br /&gt;slope = -0.5&lt;br /&gt;periodInMinutes = slope * fileCount + maxMinutes&lt;br /&gt;&lt;br /&gt;Or, as a single groovy closure:&lt;br /&gt;&lt;br /&gt;def periodToNextSweep = { fileCount, slope = -0.5, maxMinutes = 240 -&gt;&lt;br /&gt; Period.minutes( slope * fileCount + maxMinutes )&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You might think there is a danger in allowing the returned period to be a negative number of minutes.  But in all practicality this is acceptable because the objective is to determine the next instant when a sweep should occur.  If this time is in the past, simply do it now.  Of coarse you would design the slope to target the worst case, so a negative time should seldom if ever occur.   And the good part is that the parameters are easy to modify to fit changing environments.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Testing with a Fixed Time Source&lt;/span&gt;: My previous entries have demonstrated using TimeSource and Clock tied to the System clock.  But, for these tests, I think a fixed time source would be more appropriate.  The syntax is like this:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;millis = 1234920035991L // 2009-02-17T17:20:35.991-08:00 Tuesday...&lt;br /&gt;timeSource = TimeSource.fixed( Instant.millisInstant( millis ) )&lt;br /&gt;clock = Clock.clockDefaultZone( timeSource )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So now when I create a date, time or date/time object from the clock, the time is always the same.  Not very meaningful for real life, but great for testing. &lt;br /&gt;&lt;br /&gt;If I create a class that uses clock, I can inject the TimeSource based on the system clock.  And for testing, I can inject a fixed TimeSource, run tests, and not have to worry about the specific time, but simply base my tests on a static source. Here is the class:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;class SweepController {&lt;br /&gt; def clock = Clock.systemDefaultZone()&lt;br /&gt; def slope = -0.5&lt;br /&gt; def maxMinutes = 240&lt;br /&gt; &lt;br /&gt; def periodToNextSweep = { fileCount -&gt;&lt;br /&gt;  int x = (int)(slope * fileCount + maxMinutes)&lt;br /&gt;  x &lt; 0 ? Period.ZERO : Period.minutes( x )&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; def nextSweepTime = { fileCount -&gt;&lt;br /&gt;  def period = periodToNextSweep( fileCount )&lt;br /&gt;  &lt;br /&gt;  clock.offsetDateTime() + period&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And here is the test script:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;millis = 1235721600000L // 2009-02-27T00:00-08:00&lt;br /&gt;fixed = TimeSource.fixed( Instant.millisInstant( millis ) )&lt;br /&gt;clock = Clock.clockDefaultZone( fixed )&lt;br /&gt;&lt;br /&gt;sweep = new SweepController( clock:clock )&lt;br /&gt;println "now -&gt; ${clock.offsetDateTime()}"&lt;br /&gt;source = [ &lt;br /&gt; [ 480, '2009-02-27T00:00-08:00' ],&lt;br /&gt; [ 0, '2009-02-27T04:00-08:00' ],&lt;br /&gt; [ 240, '2009-02-27T02:00-08:00' ],&lt;br /&gt; [ 120, '2009-02-27T03:00-08:00' ],&lt;br /&gt;]&lt;br /&gt;source.each { count, value -&gt;&lt;br /&gt; println "count: ${count} -&gt; ${sweep.periodToNextSweep( count )}, ${sweep.nextSweepTime( count )}"&lt;br /&gt; assert value == sweep.nextSweepTime( count ).toString()&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When I run the script, here is what I get:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;now -&gt; 2009-02-27T00:00-08:00&lt;br /&gt;count: 480 -&gt; PT0S, 2009-02-27T00:00-08:00&lt;br /&gt;count: 0 -&gt; PT240M, 2009-02-27T04:00-08:00&lt;br /&gt;count: 240 -&gt; PT120M, 2009-02-27T02:00-08:00&lt;br /&gt;count: 120 -&gt; PT180M, 2009-02-27T03:00-08:00&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The main advantage is that I can run this independent of the current date, but still use the clock object without changing anything inside the class.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Conclusion&lt;/span&gt;: This quick look at Period and PeriodParser to see how it fits into the JSR-310 from the groovy coder's perspective.  Next time well look closer at Date, Time and DateTime math capabilities and how they work with groovy plus/minus operator overloading.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-4554289990421047040?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/4554289990421047040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=4554289990421047040' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/4554289990421047040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/4554289990421047040'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/02/jsr-310-javaxtime-periods.html' title='JSR-310 javax.time Periods'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_7lr-p1KyMn0/SagbqFAqLUI/AAAAAAAAAEM/zmSHxFD13as/s72-c/graph.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-636713708396692923</id><published>2009-02-22T09:00:00.000-08:00</published><updated>2009-02-22T09:00:02.398-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jdk-7'/><category scheme='http://www.blogger.com/atom/ns#' term='java jsr-310'/><category scheme='http://www.blogger.com/atom/ns#' term='javax.time'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Adjusting Date and Time with javax.time</title><content type='html'>Java's &lt;a href="https://jsr-310.dev.java.net/"&gt;JSR-310&lt;/a&gt; date and time API, co-lead by &lt;a href="http://weblogs.java.net/blog/mister__m/"&gt;Michael Nascimento Santos&lt;/a&gt; and &lt;a href="http://skillsmatter.com/expert/java-jee/stephen-colebourne"&gt;Stephen Colebourne&lt;/a&gt; is a natural spinoff from the venerable joda time.   The implementation has many advantages over java util's Date and Calendar.  Compared to java.util.Date the API has many types of Date and Time including LocalDate, LocalTime, LocalDateTime, OffsetDate, OffsetTime, OffsetDateTime, and ZonedDateTime.&lt;br /&gt;&lt;br /&gt;The new JSR-310 date, time, and date/time classes are immutable and thread safe, unlike java.util.Calendar, but at the same time they offer many basic math, adjusters, and matchers that enable date and time calculations missing in java.util.Date.   They are created from a TimeSource that can be tied to the System clock, fixed, or offset in time.  This makes the classes extremely test friendly.&lt;br /&gt;&lt;br /&gt;This entry discusses how the basic date/time math work and how the date and time adjusters can be used to solve common problems.   Lets look first at the basic math.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Look Ma, no Setters&lt;/span&gt;:   The JSR-310 date/time classes are immutable and thread safe.  To accomplish this, the API doesn't include any setXX methods.  Days, Hours, Years, etc are manipulated through "plus", "minus", and "with" methods that return new objects of the same type.  Here are a few examples, first with date then time:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;// tomorrow and 5 years from now&lt;br /&gt;clock = Clock.systemDefaultZone()&lt;br /&gt;today = clock.today()&lt;br /&gt;assert clock.tomorrow() == today.plusDays(1)&lt;br /&gt;fiveYearsFromNow = today.plusYears( 5 )&lt;br /&gt;assert today.plusMonths( 60 ).year == fiveYearsFromNow.year&lt;br /&gt;&lt;br /&gt;// two hours ago&lt;br /&gt;now = clock.timeToSecond()&lt;br /&gt;twoHoursAgo = now.minusHours( 2 )&lt;br /&gt;assert now.minusMinutes( 120 ) == twoHoursAgo&lt;br /&gt;&lt;br /&gt;// use offset datetime to get today at noon&lt;br /&gt;noon = clock.offsetDateTime().withTime( 12, 0, 0 )&lt;br /&gt;assert noon.hourOfDay == 12&lt;br /&gt;assert noon.minuteOfHour == 0&lt;br /&gt;assert noon.secondOfMinute == 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Adjusters&lt;/span&gt;:  Here is a tricky problem: how do you compute the specific date of a week numbered day of the month, for example the 3rd friday or 4th tuesday.  Bay area residents know that not being able to calculate these simple problems can cost real money (street sweep adys).  So here is how JSR-310 handles this:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;dt = clock.offsetDate()&lt;br /&gt;&lt;br /&gt;thirdFridayAdjuster = DateAdjusters.dayOfWeekInMonth( 3, DayOfWeek.FRIDAY )&lt;br /&gt;fourthTuesdayAdjuster = DateAdjusters.dayOfWeekInMonth( 4, DayOfWeek.TUESDAY )&lt;br /&gt;&lt;br /&gt;thirdFriday = dt.with( thirdFridayAdjuster )&lt;br /&gt;println "third friday -&gt; ${thirdFriday}, ${thirdFriday.toDayOfWeek()}, ${thirdFridayAdjuster}"&lt;br /&gt;assert DayOfWeek.FRIDAY == thirdFriday.toDayOfWeek()&lt;br /&gt;&lt;br /&gt;fourthTuesday = dt.with( fourthTuesdayAdjuster )&lt;br /&gt;println "fourth tuesday -&gt; ${fourthTuesday}, ${fourthTuesday.toDayOfWeek()}, ${fourthTuesdayAdjuster}"&lt;br /&gt;assert DayOfWeek.TUESDAY == fourthTuesday.toDayOfWeek()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;These examples just scratch the surface of the many date/time manipluation methods for JSR-310's javax.time package.  The next entry will discuss how JSR-310 matchers help determine if a date lands on a leap year, leap day, last day of the month, etc.  and how to use this in groovy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-636713708396692923?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/636713708396692923/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=636713708396692923' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/636713708396692923'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/636713708396692923'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/02/adjusting-date-and-time-with-javaxtime.html' title='Adjusting Date and Time with javax.time'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-3190540225442316905</id><published>2009-02-21T11:00:00.000-08:00</published><updated>2009-02-26T20:56:26.981-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jdk-7'/><category scheme='http://www.blogger.com/atom/ns#' term='java jsr-310'/><category scheme='http://www.blogger.com/atom/ns#' term='javax.time'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>JSR-310 javax.time and Groovy Tests</title><content type='html'>The development and implementation of &lt;a href="https://jsr-310.dev.java.net/"&gt;JSR-310&lt;/a&gt; is continuing to move ahead at a good pace.   Once delivered, (hopefully by &lt;a href="https://jdk7.dev.java.net/"&gt;JDK 7&lt;/a&gt;) the java community will have a solid replacement for the outdated and comparatively buggy Date and Calendar classes.   At the same time, I have created a series of groovy tests that exercise the JSR-310 capabilities.&lt;br /&gt;&lt;br /&gt;Formal unit tests for &lt;a href="https://jsr-310.dev.java.net/"&gt;JSR-310&lt;/a&gt; are written in testng and cover all the low level details including TimeZone resolving, serialization, etc.   My tests simply verify a subset of the current API and demonstrate use with groovy.  The tests cover the basic API, adjusters, matchers, peroids, formatters and parsers.  This post demonstrates the basic tests.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Basic API Tests&lt;/span&gt;:  These tests take advantage of groovy's invokeMethod() to test methods that take zero or more parameters.   They demonstrate typical use of TimeSource, Clock, LocalDate, and LocalTime classes.   With these basics it's easy to extend to LocalDateTime, OffsetDate, OffsetTime, or ZonedDateTime.  Here is the output trace generated from running BasicTests.groovy:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;TimeSource to Date/Calendar conversion tests ---------------------------------------&lt;br /&gt;Timesource.system().instant() -&gt; {ts.instant()}&lt;br /&gt;Clock Method Tests ----------------------------------------------------------------&lt;br /&gt;clock = Clock.systemDefaultZone() -&gt; TimeSourceClock[SystemTimeSource, America/Los_Angeles]&lt;br /&gt;Clock methods...&lt;br /&gt;method: clock.getSource() -&gt; SystemTimeSource&lt;br /&gt;method: clock.getZone() -&gt; America/Los_Angeles&lt;br /&gt;method: clock.today() -&gt; 2009-02-21&lt;br /&gt;method: clock.tomorrow() -&gt; 2009-02-22&lt;br /&gt;method: clock.yesterday() -&gt; 2009-02-20&lt;br /&gt;method: clock.dateTime() -&gt; 2009-02-21T08:56:35.512&lt;br /&gt;method: clock.dateTimeToMinute() -&gt; 2009-02-21T08:56&lt;br /&gt;method: clock.dateTimeToSecond() -&gt; 2009-02-21T08:56:35&lt;br /&gt;method: clock.time() -&gt; 08:56:35.515&lt;br /&gt;method: clock.timeToMinute() -&gt; 08:56&lt;br /&gt;method: clock.timeToSecond() -&gt; 08:56:35&lt;br /&gt;method: clock.offsetDate() -&gt; 2009-02-21-08:00&lt;br /&gt;method: clock.offsetTime() -&gt; 08:56:35.517-08:00&lt;br /&gt;method: clock.offsetTimeToMinute() -&gt; 08:56-08:00&lt;br /&gt;method: clock.offsetTimeToSecond() -&gt; 08:56:35-08:00&lt;br /&gt;method: clock.offsetDateTime() -&gt; 2009-02-21T08:56:35.518-08:00&lt;br /&gt;method: clock.zonedDateTime() -&gt; 2009-02-21T08:56:35.521-08:00 America/Los_Angeles&lt;br /&gt;method: clock.zonedDateTimeToMinute() -&gt; 2009-02-21T08:56-08:00 America/Los_Angeles&lt;br /&gt;method: clock.zonedDateTimeToSecond() -&gt; 2009-02-21T08:56:35-08:00 America/Los_Angeles&lt;br /&gt;method: clock.year() -&gt; Year=2009&lt;br /&gt;method: clock.yearMonth() -&gt; 2009-02&lt;br /&gt;&lt;br /&gt;OffsetDate methods from offsetDate = clock.offsetDate() -&gt; 2009-02-21-08:00&lt;br /&gt;method: offsetDate.atMidnight() -&gt; 2009-02-21T00:00-08:00&lt;br /&gt;method: offsetDate.getDayOfMonth() -&gt; 21&lt;br /&gt;method: offsetDate.getDayOfWeek() -&gt; DayOfWeek=SATURDAY&lt;br /&gt;method: offsetDate.getDayOfYear() -&gt; 52&lt;br /&gt;method: offsetDate.getMonthOfYear() -&gt; MonthOfYear=FEBRUARY&lt;br /&gt;method: offsetDate.getYear() -&gt; 2009&lt;br /&gt;method: offsetDate.toDayOfMonth() -&gt; DayOfMonth=21&lt;br /&gt;method: offsetDate.toDayOfWeek() -&gt; DayOfWeek=SATURDAY&lt;br /&gt;method: offsetDate.toDayOfYear() -&gt; DayOfYear=52&lt;br /&gt;method: offsetDate.toLocalDate() -&gt; 2009-02-21&lt;br /&gt;method: offsetDate.toMonthOfYear() -&gt; MonthOfYear=FEBRUARY&lt;br /&gt;method: offsetDate.toYear() -&gt; Year=2009&lt;br /&gt;method: offsetDate.getDate() -&gt; 2009-02-21&lt;br /&gt;&lt;br /&gt;LocalDate methods from localDate = LocalDate.date(2019, 03, 01) -&gt; 2019-03-01&lt;br /&gt;method: localDate.atMidnight() -&gt; 2019-03-01T00:00&lt;br /&gt;method: localDate.getDayOfMonth() -&gt; 1&lt;br /&gt;method: localDate.getDayOfWeek() -&gt; DayOfWeek=FRIDAY&lt;br /&gt;method: localDate.getDayOfYear() -&gt; 60&lt;br /&gt;method: localDate.getMonthOfYear() -&gt; MonthOfYear=MARCH&lt;br /&gt;method: localDate.getYear() -&gt; 2019&lt;br /&gt;method: localDate.toDayOfMonth() -&gt; DayOfMonth=1&lt;br /&gt;method: localDate.toDayOfWeek() -&gt; DayOfWeek=FRIDAY&lt;br /&gt;method: localDate.toDayOfYear() -&gt; DayOfYear=60&lt;br /&gt;method: localDate.toLocalDate() -&gt; 2019-03-01&lt;br /&gt;method: localDate.toMonthOfYear() -&gt; MonthOfYear=MARCH&lt;br /&gt;method: localDate.toYear() -&gt; Year=2019&lt;br /&gt;method: localDate.getMonthDay() -&gt; --03-01&lt;br /&gt;method: localDate.getYearMonth() -&gt; 2019-03&lt;br /&gt;method: localDate.isLeapYear() -&gt; false&lt;br /&gt;method: localDate.toEpochDays() -&gt; 17956&lt;br /&gt;method: localDate.toModifiedJulianDays() -&gt; 58543&lt;br /&gt;method: localDate.toDateTimeFields() -&gt; {ISO.Year=2019, ISO.MonthOfYear=3, ISO.DayOfMonth=1}&lt;br /&gt;&lt;br /&gt;LocalTime methods, from localTime = clock.time() -&gt; 08:56:35.549&lt;br /&gt;method: localTime.getHourOfDay() -&gt; 8&lt;br /&gt;method: localTime.getMinuteOfHour() -&gt; 56&lt;br /&gt;method: localTime.getSecondOfMinute() -&gt; 35&lt;br /&gt;method: localTime.toDateTimeFields() -&gt; {ISO.HourOfDay=8, ISO.MinuteOfHour=56, ISO.SecondOfMinute=35, ISO.NanoOfSecond=549000000}&lt;br /&gt;method: localTime.toHourOfDay() -&gt; HourOfDay=8&lt;br /&gt;method: localTime.toLocalTime() -&gt; 08:56:35.549&lt;br /&gt;method: localTime.toMinuteOfHour() -&gt; MinuteOfHour=56&lt;br /&gt;method: localTime.toSecondOfMinute() -&gt; SecondOfMinute=35&lt;br /&gt;method: localTime.getNanoOfSecond() -&gt; 549000000&lt;br /&gt;method: localTime.toNanoOfSecond() -&gt; NanoOfSecond=549000000&lt;br /&gt;method: localTime.toNanoOfDay() -&gt; 32195549000000&lt;br /&gt;End basic tests...&lt;br /&gt;&lt;/pre&gt;Close inspection reveals that some methods in LocalDate are missing in OffsetDate.  This isn't a huge problem, because OffsetDate can easily create a LocalDate object.  But, it would be nice to have isLeapYear(), toEpochDays(), toModifiedJulianDays() and other methods in both classes. &lt;span style="font-style: italic;"&gt;(This may already be in the works)...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here is a simple way to access some of the missing methods:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;offsetDate = clock.offsetDate()&lt;br /&gt;offsetDate.toLocalDate().toEpochDays()&lt;br /&gt;offsetDate.toLocalDate().toModifiedJulianDays()&lt;br /&gt;offsetDate.toLocalDate().isLeapYear()&lt;br /&gt;// or, even groovyer&lt;br /&gt;offsetDate.toLocalDate().leapYear&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;Conclusion&lt;/span&gt;:  What these tests really do is to demonstrate the basic functionality of the new javax.time classes.   It's obvious that JSR-310 offers the horsepower missing from the current java implementations.  And, it will be a very useful addition to the groovy toolkit as well.&lt;br /&gt;&lt;br /&gt;Future posts will demonstrate the Adjusters, Matchers, Periods, Formatters and Parsers.  Once the tests are cleaned up a bit, I'll post them on line.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-3190540225442316905?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/3190540225442316905/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=3190540225442316905' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3190540225442316905'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3190540225442316905'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/02/jsr-310-javaxtime-and-groovy-tests.html' title='JSR-310 javax.time and Groovy Tests'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-971743469538470355</id><published>2009-02-14T17:31:00.000-08:00</published><updated>2009-02-15T16:48:01.119-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsr-310'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='temporal'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Java Date/Time Objects with JSR-310</title><content type='html'>I spent the day experimenting with the proposed javax.time API's from &lt;a href="https://jsr-310.dev.java.net/"&gt;JSR-310&lt;/a&gt;.  Modeled after the popular joda-time the new javax.time objects offer a rich set of date and time manipulation, basic math, durations, matching and time zone awareness. &lt;br /&gt;&lt;br /&gt;At the top level, the concept of a TimeSource is introduced.  This is a replacement for using System.currentTimeMillis() or System.timeNanos() or the standard java.util.Date and Calendar contructors.  It can be spring loaded, so getting a source that has an offset is possible, which makes testing very easy.&lt;br /&gt;&lt;br /&gt;The abstract Clock class is similar to TimeSource, but it is time zone aware.  It's easy to get the local time (Clock.systemDefaultZone()) or to create a clock from a different time zone (Clock.system(TimeZone.UTC)).&lt;br /&gt;&lt;br /&gt;Once the Clock object is created you can access today(), tomorrow(), yesterday(), the current DateTime and other methods for creating complex DateTime objects.  You can also get the current instant which represents the current date/time as an instant since the epoch in nanoseconds. &lt;br /&gt;&lt;br /&gt;The first step to using javax.time is to checkout the latest version from &lt;a href="https://jsr-310.dev.java.net/source/browse/jsr-310/trunk/"&gt;subversion&lt;/a&gt;. (I'm at build version 702).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Converting Date and Calendar to javax.time&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A common way to construct both Date and Calendar from the java.util package is to use milliseconds.  The Instant class from javax.time includes a method called toEpochMillis() that makes this easy: (groovy syntax)&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;utilDate = new java.util.Date()&lt;br /&gt;instant = Instant.millisInstant( utilDate.time )&lt;br /&gt;zone = ZoneOffset.zoneOffset( -8 )&lt;br /&gt;dateTime = OffsetDateTime.dateTime( instant, zone )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Converting javax.time to Date/Calendar&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To convert a javax.time object back to either Date or Calendar is just as simple:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;ts = TimeSource.system()&lt;br /&gt;dt = new Date( ts.instant().toEpochMillis() )&lt;br /&gt;&lt;br /&gt;cal = Calendar.instance&lt;br /&gt;cal.timeInMillis = ts.instant().toEpochMillis()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Date Math&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here is a quick example of what you can do with a DateTime object.  In this case, I'll use an OffsetDateTime to express the full ISO8601 date. (again, groovy syntax)&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;clock = Clock.systemDefaultZone()&lt;br /&gt;assert clock.today().toString() == "2009-02-14"&lt;br /&gt;assert clock.tomorrow().toString() == "2009-02-14"&lt;br /&gt;&lt;br /&gt;dt = clock.offsetDateTime()&lt;br /&gt;assert dt.toString() == "2009-02-14T20:31:50.896-08:00"&lt;br /&gt;&lt;br /&gt;dt.plusYears(3).toString() == "2012-02-14T20:31:50.896-08:00&lt;br /&gt;&lt;br /&gt;assert dt.year == 2009&lt;br /&gt;year = dt.toYear()  // get the object, not the int&lt;br /&gt;&lt;br /&gt;assert year.lengthInDays() == 365&lt;br /&gt;assert year.next().value == 2010&lt;br /&gt;assert year.nextLeap().value == 2012&lt;br /&gt;assert year.nextLeap().lengthInDays() == 366&lt;br /&gt;&lt;br /&gt;yearMonth = YearMonth.yearMonth( dt )&lt;br /&gt;assert yearMonth.toString() == "2009-02"&lt;br /&gt;assert yearMonth.plusMonths(5).toString() == "2009-07"&lt;br /&gt;assert yearMonth.minusMonths(5).toString() == "2008-09"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So you get the point.  Many useful methods currently missing from the java JDK.  So, hopefully this will be complete in time to make it into java 7.  In the mean time, though not ready for production, it's available for bleeding-edge use.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-971743469538470355?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/971743469538470355/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=971743469538470355' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/971743469538470355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/971743469538470355'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2009/02/java-datetime-objects-with-jsr-310.html' title='Java Date/Time Objects with JSR-310'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-682500595502391713</id><published>2008-12-22T16:23:00.000-08:00</published><updated>2008-12-23T11:41:26.867-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RSA'/><category scheme='http://www.blogger.com/atom/ns#' term='AES'/><category scheme='http://www.blogger.com/atom/ns#' term='encryption'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='Bouncy Castle'/><title type='text'>A Groovy Cryptology Library</title><content type='html'>Rain City's open-source groovy library has recently been enhanced to include a crypto package. Implementation includes symmetric and asymmetric algorithms, hmac, digest, guid, key pool and key store utilities.  This post will explain the main classes and show a few examples.&lt;br /&gt;&lt;br /&gt;Guid: The Guid class is a simple wrapper around java's UUID class. It makes it easy to create a new guid or read an existing guid string to create a guid object.&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;println new Guid().toString() -&gt; e79a8462-32ad-48db-9905-3d53e1471de3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;SaltedSecureRandom: SaltedSecureRandom provides a salt-seeded SecureRandom object using hardware dependent /dev/urandom.   This works for all non-Microsoft-windows platforms.   On windows, a simple secure random object is returned.  (If you are serious about security, don't use windows).&lt;br /&gt;&lt;br /&gt;Digest: This class is a thin wrapper around the JCE digest algorithms, including SHA-512.  The class defaults to SHA-256 to maintain compatibility with other languages and platforms.  MD5 is also available.  The default provider is Sun.  The class also provides a 'main' to enable checking digests from the command line.&lt;br /&gt;&lt;br /&gt;ByteContainer: The ByteContainer class has utilities for converting strings to by streams, base64 bytes and hex strings.  Invaluable for exchanging encrypted data.&lt;br /&gt;&lt;br /&gt;Hmac: The Hmac class supports SHA-256 and MD5 and provides methods to set text and key to compute and render keyed digests.   It also includes a key generator that defaults to SHA-256 algorithm.&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;def bc = new ByteContainer()&lt;br /&gt;def k = Hmac.generateKey()&lt;br /&gt;&lt;br /&gt;bc.bytes = k.encoded&lt;br /&gt;println bc -&gt; 85a749fd3818fccf0dc6997b6c9f25129babbf6b98606f8b51a14e68b5551fa6&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Crypto: This is the primary class to use when you need asymmetric  encryption using well known algorithms from various providers.  The default algorithm is AES using the &lt;a href="http://www.bouncycastle.org/"&gt;Bouncy Castle&lt;/a&gt; provider with PKCS7 padding.  Other supported algorithms are Blowfish, DES, Tripple DES, and all the others supported by the Sun provider.&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;def crypto = new Crypto()  // requires bouncy castle&lt;br /&gt;&lt;br /&gt;assert Security.getProvider("BC")&lt;br /&gt;&lt;br /&gt;def pw = "my password".bytes&lt;br /&gt;def key = crypto.createKeySpec( pw )&lt;br /&gt;assert 'AES' == key.algorithm&lt;br /&gt;&lt;br /&gt;def text = 'this is a test of plain text'&lt;br /&gt;&lt;br /&gt;crypto.generateIV()&lt;br /&gt;println crypto.ivSpec.getIV()&lt;br /&gt;&lt;br /&gt;def enc = crypto.encrypt( text, pw )&lt;br /&gt;def s64 = enc.encodeBase64().toString()&lt;br /&gt;println s64 -&gt; q7teBCCBK+n3qTSr518QIj04qa/cS/P2bhsUw/eg8a8=&lt;br /&gt;&lt;br /&gt;def dec = crypto.decrypt( enc, pw )&lt;br /&gt;def s = new String( dec )&lt;br /&gt;println s -&gt; this is a test of plain text&lt;br /&gt;&lt;br /&gt;crypto.clearBytes pw&lt;br /&gt;println "${pw}" -&gt; [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;RsaKeyPool/RsaKeyStore: 2048 bit public/private keys use very large random prime numbers.   So generating secure key pairs takes lots of CPU cycles and a fair amount of time.  To alleviate any performance bottlenecks, the best approach is to create a key pool to serve up single use key pairs.  The pool actually gets keys from a key store that can be built off-line to any desired size.  This approach means that a server won't be bogged down initializing the pool when it first comes up (or is bounced).&lt;br /&gt;&lt;br /&gt;The actual generation of the key pairs is through a JCE KeyPairGenerator using the Bouncy Castle provider.  It's initialized to the key size (defaulting to 2048 bits) and uses the salted secure random class mentioned earlier.&lt;br /&gt;&lt;br /&gt;Rsa: The Rsa class provides encryption and decryption of data using public/private keys.  For the uninitiated, the main thing to remember is that the public key is used to encrypt and the private key to decrypt.  The Rsa class makes this a bit easier by providing encrypt() and decrypt() methods.   A simple use case example goes like this:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Alice wants to send an encrypted document to Bob.  She begins the process by requesting a public key from Bob.  This is what will be used to encrypt the document.&lt;/li&gt;&lt;li&gt;Bob creates a key pair (or gets a pair from the pool) and sends the public key back to Alice.&lt;/li&gt;&lt;li&gt;Alice creates a Rsa object and sets the public key to Bob's key.  The document is encrypted then sent to Bob.&lt;/li&gt;&lt;li&gt;Bob receives the encrypted document and decrypts with Rsa using his private key.&lt;/li&gt;&lt;/ul&gt;Now lets take this simple use case and implement some of the parts.  For our purposes, we will use two classes Alice, the sender and Bob the receiver.&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;import org.raincity.glib.crypto.*&lt;br /&gt;&lt;br /&gt;class Alice {&lt;br /&gt;def encrypted&lt;br /&gt;def send = { bob, text -&gt;&lt;br /&gt;def key = bob.getKey()&lt;br /&gt;def rsa = Rsa.instance&lt;br /&gt;rsa.pubKey = key&lt;br /&gt;encrypted = rsa.encrypt( text.bytes )&lt;br /&gt;bob.receive( encrypted )&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class Bob {&lt;br /&gt;def text&lt;br /&gt;def keyPair&lt;br /&gt;&lt;br /&gt;def getKey() {&lt;br /&gt;keyPair = RsaKeyPool.instance.keyPair&lt;br /&gt;return keyPair.publicKey&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;def receive = { encrypted -&gt;&lt;br /&gt;def rsa = Rsa.instance&lt;br /&gt;rsa.privKey = keyPair.privateKey&lt;br /&gt;text = new String( rsa.decrypt( encrypted ) )&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;alice = new Alice()&lt;br /&gt;bob = new Bob()&lt;br /&gt;alice.send( bob, "This is a super secret message" )&lt;br /&gt;println "encrypted=${new BigInteger(alice.encrypted).toString(16)}"&lt;br /&gt;println bob.text&lt;br /&gt;&lt;/pre&gt;And when you run the script you see...&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;encrypted=496bc13c9ba194a2becc525839bab8d9c40a7c126a1ac32f0aa21...&lt;br /&gt;decrypted=This is a super secret message&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Future posts will discuss how these classes are used to support Adobe Flex to Grails encrypted exchange sessions.   If you are interested you can download the library and examples in a single jar file &lt;a href="http://raincitysoftware.com/downloads/groovy-cryptology.jar"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-682500595502391713?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/682500595502391713/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=682500595502391713' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/682500595502391713'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/682500595502391713'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/12/groovy-cryptology-library.html' title='A Groovy Cryptology Library'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-8659416952664826332</id><published>2008-08-01T16:00:00.000-07:00</published><updated>2008-12-11T14:31:04.740-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='spread sheet'/><category scheme='http://www.blogger.com/atom/ns#' term='jxl'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><category scheme='http://www.blogger.com/atom/ns#' term='reports'/><title type='text'>Groovy Excel Report Library</title><content type='html'>I just finished a series of reports for one of my customers using grails as the backend and Flex for the user interface.  As with many of my reports for this customer, they requested the output be delivered as CSV files.  Easy, and very boring.&lt;br /&gt;&lt;br /&gt;So, with the help of &lt;a href="http://jexcelapi.sourceforge.net/"&gt;JExcel&lt;/a&gt; and the&lt;a href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/index.html"&gt; jxl APIs&lt;/a&gt; from Andy Khan,  I created an alternate output of an excel spreadsheet rather than CSV flat file.  This has the additional benefit of sending multi-dimensional results, e.g., summary and details on two separate sheets, output statistics and charting on a third.   This is much more useful for the end-user.&lt;br /&gt;&lt;br /&gt;To make life easy, I created a wrapper around the jxl libraries to make it possible to use an alternate excel API such as Apache POI.  At the same time, I created a set of conventions that use a template spreadsheet to easily create all the output without having to program any of the "view" components (i.e., classic MVC).  The controller class is Workbook and the Model data is configured in Worksheet objects.  All the formatting for dates, numbers, layout, etc. is in configured in templates (xsl files) easily configured and modified by non-programmers.&lt;br /&gt;&lt;br /&gt;Since the target for this is inside war-deployed web applications I had to configure an external templates folder to enable report format changes without having to re-war/deploy or bounce the server.  The templates folder is part of a more extensive Exchange class that coordinates remote data exchanges to a known area outside the web-space.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conventions&lt;/span&gt;:  Below is a list of convetions used to build reports...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;xls template contains all formatting and sheets for the report&lt;/li&gt;&lt;li&gt;key words embedded into the template provides cell locations where data is to be written.  values stored in the Worksheet object are used by the Workbook controller to update the spreadsheet output.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;a special type of keyword called "ReportData" is used to insert multilple rows into the spreadsheet, based on query result sets (or any list of data).&lt;/li&gt;&lt;/ul&gt;The Worksheet models also have a place holder for "options" that change/enhance the controller's behavior as it builds output.  Examples include "greenbar" to create green-bar reports, "showItemNumbers" to write out the actual row number to the left of the ReportData.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Working Example&lt;/span&gt;:  To demonstrate the report library I created a "Customer Invoice" report.   The template has three sheets, includes graphic images, and shows two of the three "ReportData" sets in green bar (and blue bar) with automatic item numbering.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Code&lt;/span&gt;: The full report can be built and run with just a few lines of code.  Here is a typical example that prints the invoice, details, and expenses for a single period.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_7lr-p1KyMn0/SJO1AwS0WGI/AAAAAAAAACY/RAgkKQWUw88/s1600-h/invoice-code.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://1.bp.blogspot.com/_7lr-p1KyMn0/SJO1AwS0WGI/AAAAAAAAACY/RAgkKQWUw88/s400/invoice-code.png" alt="" id="BLOGGER_PHOTO_ID_5229722616875735138" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;The three sheets are defined as details, expenses and invoice.  Invoice details and expense rows are fetched for a specific period with GORM in the typical way&lt;/span&gt;&lt;span&gt;.  Report fields are extracted to match the template report data layout&lt;/span&gt;&lt;span&gt;. The&lt;/span&gt;&lt;span style="font-weight: bold;"&gt; &lt;/span&gt;&lt;span&gt;main invoice sheet has a few extra named values (line 28-31) and constructs two lines of data (lines 33 &amp;amp; 34).&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;Report Output&lt;/span&gt;: Here are the three sheets created by the code above.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_7lr-p1KyMn0/SJO5FzwL94I/AAAAAAAAACg/LsEl2auq0SA/s1600-h/invoice.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://4.bp.blogspot.com/_7lr-p1KyMn0/SJO5FzwL94I/AAAAAAAAACg/LsEl2auq0SA/s400/invoice.png" alt="" id="BLOGGER_PHOTO_ID_5229727101750081410" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_7lr-p1KyMn0/SJO6vUrvh2I/AAAAAAAAACo/AXAFYoml2g4/s1600-h/details.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://2.bp.blogspot.com/_7lr-p1KyMn0/SJO6vUrvh2I/AAAAAAAAACo/AXAFYoml2g4/s400/details.png" alt="" id="BLOGGER_PHOTO_ID_5229728914476074850" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_7lr-p1KyMn0/SJO7Ej44SvI/AAAAAAAAACw/JWpBqcU23jE/s1600-h/expenses.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://4.bp.blogspot.com/_7lr-p1KyMn0/SJO7Ej44SvI/AAAAAAAAACw/JWpBqcU23jE/s400/expenses.png" alt="" id="BLOGGER_PHOTO_ID_5229729279334959858" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-8659416952664826332?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/8659416952664826332/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=8659416952664826332' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/8659416952664826332'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/8659416952664826332'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/08/groovy-excel-report-library.html' title='Groovy Excel Report Library'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_7lr-p1KyMn0/SJO1AwS0WGI/AAAAAAAAACY/RAgkKQWUw88/s72-c/invoice-code.png' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-1849718872329987107</id><published>2008-07-26T12:03:00.000-07:00</published><updated>2008-07-26T12:33:51.313-07:00</updated><title type='text'>Dynamic Grails Application Configuration</title><content type='html'>If you deploy your grails applications with war files you may find it necessary implement an application configuration mechanism that lives outside the web-space.  My first though was to use and external file, probably xml and read it in when needed.  But, I decided that persisting configurations to the database provided a much more universal solution.  It also avails itself to adding a UI or acting as a remote web-service.&lt;br /&gt;&lt;br /&gt;The Domain Object, AppConfig is very simple.  It has a name, parameter, value and status:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;class AppConfig {&lt;br /&gt; String name&lt;br /&gt; String parameter&lt;br /&gt; String value&lt;br /&gt; String status&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The "name" and "parameter" columns uniquely identify the configuration setting and the "value" is a string representation of the parameter's value.  The status can be anything, but I find it useful to set the target environment, e.g., 'dev' or 'test'.  I usually use the class name as the configuration name, so looping through a set of parameters is as easy as this:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;AppConfig.findAllByName( this.class.name ).each { p -&gt;&lt;br /&gt; switch(p.parameter) {&lt;br /&gt;   case 'enabled': enabled = (p.value == 'true' ? true : false)&lt;br /&gt;   case 'other': other = p.value&lt;br /&gt;   case 'anInt: myInt = Integer.parseInt( p.value )&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I use a standard dataset loader to read in and insert settings on application startup taking care not to clobber existing values.   Configurations are read in during development, test and production.&lt;br /&gt;&lt;br /&gt;A typical use is to set parameters for Quartz Jobs.  This way, the parameters are read each time the job starts to dynamically configure actions.  The most common is to enable or disable the job.  This comes in handy if a job goes bad but you can't bring down the web server.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-1849718872329987107?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/1849718872329987107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=1849718872329987107' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/1849718872329987107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/1849718872329987107'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/07/dynamic-grails-application.html' title='Dynamic Grails Application Configuration'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-8475794729320146031</id><published>2008-07-16T21:30:00.000-07:00</published><updated>2008-12-23T10:47:47.166-08:00</updated><title type='text'>Groovy Polymorphics with Closures</title><content type='html'>Let me begin by stating the problem: I need an object that can locate files in a specific folder, parse the selected files, then execute database updates against the parsed results.  The twist is that the update process might be inside a grails application using GORM, or, it might be a standalone jdbc process that has it's own data access mechanism.  Here is the proposed object:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;class Process {&lt;br /&gt; def findFiles() { /* return a list of files */ }&lt;br /&gt; def parseFile(file) { /* parse the file, return the data */ }&lt;br /&gt;&lt;br /&gt; // the update function (closure)&lt;br /&gt; def update&lt;br /&gt;&lt;br /&gt; // the runner&lt;br /&gt; def run() {&lt;br /&gt;   def list = findFiles()&lt;br /&gt;   records = []&lt;br /&gt;   list.each { records &lt;&lt; parseFile( it ) }    &lt;br /&gt;   update( records )  &lt;br /&gt; }&lt;br /&gt;}  &lt;/pre&gt;Forget about the empty methods for now, let's just assume they work.  Notice that run() finds the files, parses them, then updates them.  Here is how the target object is used:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;def process = new Process()&lt;br /&gt;process.update = (isGorm ? gormUpdater : jdbcUpdater)&lt;br /&gt;process.run()&lt;/pre&gt;The first thing you might notice is that 'update'  in the Process class has no body--it's simply a tag for the closure name.   When the class becomes an object, update is assigned the closure method based on the runtime environment.  Here is how a very simple GORM closure would be defined:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;myGormUpdater = { records -&gt;&lt;br /&gt;records.each { row -&gt;&lt;br /&gt;  row.save()&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;Simple.  Loop through the records and save each one.  The polymorphic magic is  implemented with a closure, not a sub class.  This eliminates class bloat by using portable stand-alone methods assigned to normal variables of a class--like named function pointers.  An added benefit is that the closure can be used on similar classes that require the same functionality.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Real World Scenario&lt;/span&gt;:  Lets say I have a small group of software engineers.  Two of my engineers are data-feed experts, another three are database domain experts.  Me, well, I'm the manager.&lt;br /&gt;&lt;br /&gt;So, since I have the big picture, I design the base class and create test fixture data.  Then, I tell my feed experts to give me methods for parsing a set of files.  They don't have to worry about where the files are, just how to parse and create datasets.  At the same time I assign the database ops to the database group.  They don't have to concern themselves with where the data comes from, just how to convert datasets into database updates for all our target databases.&lt;br /&gt;&lt;br /&gt;Even though I'm a manager, I contribute in a real way to the code base.  And, to keep my team productive, I create the test data for the parser and database groups.  The experts are assigned tasks that they handle best.&lt;br /&gt;&lt;br /&gt;Combine this with daily scrum communications and, for the hands-on CTO, this is very Groovalicious!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-8475794729320146031?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/8475794729320146031/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=8475794729320146031' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/8475794729320146031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/8475794729320146031'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/07/groovy-polymophics-with-closures.html' title='Groovy Polymorphics with Closures'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-739897254439755704</id><published>2008-07-15T09:00:00.000-07:00</published><updated>2008-07-15T09:00:01.695-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flex'/><category scheme='http://www.blogger.com/atom/ns#' term='file upload'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><title type='text'>Flex on Grails</title><content type='html'>Borrowing from Flex/Rails libraries created about a year ago, I have created a Flex/Grails application using similar HTTP/XML protocols.  The application requests report data via HTTP, the server creates the report, persisting it to a file, then the client pulls the data back with a file request.  This was the only way I could get Flex/Flash to open a "file-save" dialog to let the user write to their local hard-drive.&lt;br /&gt;&lt;br /&gt;I used Flex's &lt;a href="http://livedocs.adobe.com/flex/3/html/help.html?content=data_access_2.html"&gt;HTTPService&lt;/a&gt; object to do the initial report request, then &lt;a href="http://livedocs.adobe.com/flex/3/langref/flash/net/FileReference.html"&gt;FileReference&lt;/a&gt; with additional &lt;a href="http://livedocs.adobe.com/flex/3/langref/flash/net/URLVariables.html"&gt;URLVariables&lt;/a&gt; parameters to retrieve the file.  Grails/GORM made the data retrieval easy and the controller/service packages were simple implementations.  This was comparable to Flex/Rails implementation that I worked on a year ago.&lt;br /&gt;&lt;br /&gt;The server side runs in a war distribution so the output file had to exist outside the webspace.  The Flex application requests the file with an HTTP post using &lt;a href="http://livedocs.adobe.com/flex/3/langref/flash/net/URLRequest.html"&gt;URLRequest&lt;/a&gt; with specific parameters (URLVariables) to retrieve the file.  Because the server is deployed with only war files, the request is made to a specific controller with a "getfile" action that retrieves the file from someplace on the server's drive (obviously not in the war file) and is consistent with where the file was originally written.  Once the file is returned to the client it is removed, so it can only be read back a single time.&lt;br /&gt;&lt;br /&gt;This first iteration returns a CSV file, but creating a full xls spreadsheet or a PDF document would be easy to implement.  There is also an xml document that can be returned to enable showing the report in a local viewer, e.g., a data grid.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-739897254439755704?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/739897254439755704/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=739897254439755704' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/739897254439755704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/739897254439755704'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/07/flex-on-grails.html' title='Flex on Grails'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-4914654510278270906</id><published>2008-07-10T10:00:00.000-07:00</published><updated>2008-07-10T10:00:01.114-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gorm'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Replacing GORM with Groovy Sql for Lightwieght Jobs</title><content type='html'>&lt;a href="http://grails.org/GORM"&gt;GORM&lt;/a&gt;, the Grails Object-Relational Mapping persistence framework provides a rational way to use Hibernate based on pojo (or groovy pogo) domain classes and basic conventions.    Create a domain class, define some constraints, start up grails and viola! tables, columns, indexes are created without coding a line of SQL/DDL or creating annoying XML mappings.&lt;br /&gt;&lt;br /&gt;But, what if you have a simple standalone process that requires single or multiple database access?  Currently GORM has trouble working outside of grails, and creating a new grails project just to support a simple, no-client script is overkill.  For that, I use  groovy's database access classes in &lt;a href="http://groovy.codehaus.org/api/groovy/sql/package-summary.html"&gt;groovy.sql&lt;/a&gt; and a small custom library to easily access multiple data sources and provide simple query tools optimized for batch operations.&lt;br /&gt;&lt;br /&gt;Here's an example: Tracking multiple UPS shipments.  The application must access existing shipment requests from a local database, then query the UPS tracking remote service to discover shipment status.  When a shipment is picked up or delivered, another local database is updated and a third system is updated via XML feed.&lt;br /&gt;&lt;br /&gt;To summarize, the requirements are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;provide simultaneous access to multiple databases&lt;/li&gt;&lt;li&gt;query UPS tracking via HTTP requests&lt;/li&gt;&lt;li&gt;create XML responses and write to a messaging system&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Cron triggers this process multiple times during the working day.   It runs on the production machine (1MB slice at slicehost), so should be kind to existing web and database applications serving the user base.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Design Implementation:&lt;/span&gt;&lt;br /&gt;As always, I start with the tests.   Using groovy test case, the database queries, inserts, and updates tests were created, then the target classes and methods.   Queries are always the easiest, and updates are straight forward as well.  Inserts with auto generated IDs are a bit tricker, but here is where groovy sql comes to the rescue.&lt;br /&gt;&lt;br /&gt;Creating new tables presented a new problem: should I create SQL/DDL scripts?  No!  For this, it was easy enough to access the current grails application and create the new domain classes, run grails to have Hibernate create the new tables.&lt;br /&gt;&lt;br /&gt;The groovy &lt;a href="http://groovy.codehaus.org/api/groovy/sql/Sql.html"&gt;Sql class&lt;/a&gt; provides a method called &lt;a href="http://groovy.codehaus.org/api/groovy/sql/Sql.html#executeInsert%28groovy.lang.GString%29"&gt;executeInsert()&lt;/a&gt;.  The magic in this method is that it returns an array of all auto-generated keys.  So, if I'm working in MySql or Oracle or whatever, inserts all look the same--no database specific code required.&lt;br /&gt;&lt;br /&gt;Database access defaults to the production environment but database tests must operate on the test database (similar to grails).  This is easy to accomplish by simply overriding the default behavior in the test scripts.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conclusions:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Use groovy Sql for small, non-web applications that require single or multiple database access.&lt;/li&gt;&lt;li&gt;Use Grails/GORM for all database/table creation and maintenance (no DDL scripts).  This also has the additional benefit of enabling access to new tables through grails if required.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Use executeInsert() to return auto-generated keys when inserting new rows.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-4914654510278270906?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/4914654510278270906/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=4914654510278270906' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/4914654510278270906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/4914654510278270906'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/07/replacing-gorm-with-groovy-sql-for.html' title='Replacing GORM with Groovy Sql for Lightwieght Jobs'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-3642433355960462423</id><published>2008-05-09T15:39:00.000-07:00</published><updated>2008-05-09T15:59:29.462-07:00</updated><title type='text'>MySQL InnoDB Dialect</title><content type='html'>I missed a configuration detail in conf/DataSource.groovy prior to going to production on a recent grails project.  I didn't notice the damage until I looked closely at the backup script created by mysqldump.  The re-build script called out ENGINE=MyISAM rather than InnoDB.  So, no transactions, cascading, or other basic features.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Fix&lt;/span&gt;: To correct the problem, I inserted the following into the hibernate section:&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;dialect='org.hibernate.dialect.MySQL5InnoDBDialect'&lt;/pre&gt;When I ran my tests, there were a couple of changes that need to be made mainly because transactions were now working correctly.   The next step was to repair the production database.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Repair&lt;/span&gt;:  The sql rebuild script needed to be modified to replace MyISAM with InnoDB--a perfect job for groovy.   Here is the script:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;#!/usr/bin/env groovy&lt;br /&gt;file = new File( 'production.sql' )&lt;br /&gt;&lt;br /&gt;new File('fix.sql').withPrintWriter { writer -&gt;&lt;br /&gt;file.eachLine { line -&gt;&lt;br /&gt;  writer.println(line.replace('MyISAM', 'InnoDB'))&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Simple to implement.  Very groovy, and now with transactions!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-3642433355960462423?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/3642433355960462423/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=3642433355960462423' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3642433355960462423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3642433355960462423'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/05/mysql-innodb-dialect.html' title='MySQL InnoDB Dialect'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-4766172319956615321</id><published>2008-05-08T18:58:00.000-07:00</published><updated>2008-05-08T19:25:20.001-07:00</updated><title type='text'>Alternate Logging in Grails</title><content type='html'>For most cases, the Grails logging system, based on log4j, works fine, especially for development.  But when in production, if you deploy using a single jar, it gets a bit trickier when trying to enable and disable logging.  So, to that end, I have created a quick and dirty logging system using groovy's advanced file features.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Groovy Files&lt;/span&gt;: In java, log files are simple to configure, but you end up writing the same old try/catch/finally code--very old style.  In groovy, all you need is a file name and you are good to go.  Here is a groovy example:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;def file = new File("test.log")&lt;br /&gt;file.write("this")&lt;br /&gt;&lt;/pre&gt;No try/catch.  No worrying about opening or closing.  Just name it and write to it.  Very groovy.&lt;br /&gt;&lt;br /&gt;Applications Log Files: Each application has it's own specific configuration.  So, in the grails-app/conf folder is the perfect place an for ApplicationConfig class to hold the application's configurations.  Inside this class are a few methods that define the external logging system.  The methods include getLogDirectory(), getLogProperties(), getLogfile(name), etc.  For our purposes, getLogfile() is the only method we need to invoke.  Again, an example:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;def logger = new ApplicationConfig().getLogfile('my-service.log')&lt;br /&gt;logger?.append("my message")&lt;br /&gt;&lt;/pre&gt;And that's it.  So, logger? only appends the message if a log file exists.  The log file is external to the web container, and easy to create (touch my-service.log) or remove.  If it exists, the it gets logged to.  If not, then no logging occurs.  The sys-admin, armed with a list of available log file targets, can create or remove logs on the fly without touching the web server.  Again, very groovy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-4766172319956615321?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/4766172319956615321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=4766172319956615321' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/4766172319956615321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/4766172319956615321'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/05/alternate-logging-in-grails.html' title='Alternate Logging in Grails'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-3775435070683532439</id><published>2008-03-06T07:00:00.000-08:00</published><updated>2008-03-06T10:41:03.151-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jetty'/><category scheme='http://www.blogger.com/atom/ns#' term='fedora'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='slicehost'/><title type='text'>Grails Application Deployed to Slicehost</title><content type='html'>We deployed our beta application on slicehost virtual server.  The slice is 1GB using fedora 7, mysql 5.1, jetty 6.1.6 and resin 3.2.  Groovy version 1.5.4 is installed on the slice and we used Grails 1.0.1 for development, but it's not installed on the slice.&lt;br /&gt;&lt;br /&gt;The application at this point is minimal, but is scheduled to go live later this month.  I can't go into to much detail, but application provides a data service between United Parsel Service and a major customer to schedule shipments of large and small freight.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-3775435070683532439?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/3775435070683532439/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=3775435070683532439' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3775435070683532439'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3775435070683532439'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/03/grails-application-deployed-to.html' title='Grails Application Deployed to Slicehost'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-552199889828450400</id><published>2008-03-04T11:58:00.000-08:00</published><updated>2008-03-06T12:35:28.505-08:00</updated><title type='text'>Groovy DateTime Math</title><content type='html'>&lt;a href="http://raincitysoftware.blogspot.com/2008/03/groovy-temporal-support-continued.html"&gt;DateTime&lt;/a&gt; is capable of simple increment (++), decrement (--), and all the add/roll methods provided by Calendar.  Here is an example of some of the ops:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;refDate = new Date()&lt;br /&gt;dt = new DateTime(date:refDate)&lt;br /&gt;tomorrow = dt + 1&lt;br /&gt;yesterday = dt - 1&lt;br /&gt;&lt;br /&gt;assert refDate == dt // insure that the original did not change&lt;br /&gt;assert refDate + 1 == tomorrow&lt;br /&gt;assert refDate - 1 == yesterday&lt;br /&gt;assert refDate + 7 == nextWeek&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;Groovy Equality&lt;/span&gt;: Groovy evaluates '==' as equality, not identity.  So it was necessary to override equals() to compare to the millisecond and return true or false.  The override makes it possible to return true when the underlying milliseconds match whether from Date, Calendar, Long, or DateTime.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Problems with Milliseconds&lt;/span&gt;: When working through the plus and minus methods I made the mistake of using the raw milliseconds to add days.  This worked fine on my linux machine, but mysteriously died on OSX.  What I neglected to take into consideration was the switch between standard and daylight time (luckily my development cycle is close to the change or I would have missed it).  So, a quick fix was to rely on java.util.Date to add days and return a new DateTime copy.  A better approach may be to use groovy's Duration classes...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Durations&lt;/span&gt;: The groovy library includes a set of &lt;a href="http://groovy.codehaus.org/api/groovy/time/Duration.html"&gt;duration classes&lt;/a&gt; in the groovy.time package.  The duration classes are containers for years, months, days, hours, minutes, seconds and millis.  They support basic math functions to add and subtract durations--very groovy.  DateTime uses the duration classes for basic math with the following methods:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;now = new DateTime()&lt;br /&gt;current = now.asDuration()&lt;br /&gt;twodays = new Duration(2, 0, 0, 0, 0)&lt;br /&gt;dt = DateTime.fromDuration( current + todays )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Combined with groovy's &lt;a href="http://groovy.codehaus.org/api/org/codehaus/groovy/runtime/TimeCategory.html"&gt;TimeCategory&lt;/a&gt;, you can do this:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;use(TimeCategory) {&lt;br /&gt;  future = new DateTime(date:2.weeks.from.now)&lt;br /&gt;  oneWeek = 1.week  // this is a duration&lt;br /&gt;&lt;br /&gt;  // schedule something once each week for the next 5 weeks...&lt;br /&gt;  dates = []&lt;br /&gt;  5.times { dates &lt;&lt; it.weeks.from.now }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Additional utility methods and parsing to come next...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-552199889828450400?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/552199889828450400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=552199889828450400' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/552199889828450400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/552199889828450400'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/03/groovy-datetime-math.html' title='Groovy DateTime Math'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-2380532399080613935</id><published>2008-03-04T10:30:00.001-08:00</published><updated>2008-03-04T11:28:38.614-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='datetime'/><category scheme='http://www.blogger.com/atom/ns#' term='temporal'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Groovy DateTime Comparisons</title><content type='html'>Those of you following my posts know that I'm working on &lt;a href="http://raincitysoftware.blogspot.com/2008/03/groovy-temporal-support-classes.html"&gt;enhanced date/time support&lt;/a&gt; for groovy.  In a &lt;a href="http://raincitysoftware.blogspot.com/2008/03/groovy-temporal-support-continued.html"&gt;previous post&lt;/a&gt;, I discussed a new DateTime class that leverages the power of java's Calendar and updates the interface in a groovy way.  Which brings me to after(), before(), and compareTo() methods.&lt;br /&gt;&lt;br /&gt;Calendar implements compareTo() that accepts only a calendar object.  If you pass it a Date and exception is thrown.  How restrictively lame.&lt;br /&gt;&lt;br /&gt;DateTime's compareTo() accepts multiple types that can be evaluated to the millisecond level.  So, DateTime, Date, Calendar, even long types are accepted.  This approach enables this to work:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;&lt;br /&gt;now = [ new Date(), Calendar.getInstance(), System.currentTimeMillis, new DateTime() ]&lt;br /&gt;&lt;br /&gt;yesterday = new DateTime() - 1&lt;br /&gt;tomorrow = new DateTime() + 1&lt;br /&gt;later = new DateTime(hours:23, minutes:59)&lt;br /&gt;&lt;br /&gt;now.each {&lt;br /&gt; assert yesterday.before( it )&lt;br /&gt; assert tomorrow.after( it )&lt;br /&gt; assert laster.after( it )&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;DateTime.  How groovy is that!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-2380532399080613935?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/2380532399080613935/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=2380532399080613935' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2380532399080613935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2380532399080613935'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/03/groovy-datetime-comparisons.html' title='Groovy DateTime Comparisons'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-2900207716232448649</id><published>2008-03-04T09:33:00.000-08:00</published><updated>2008-03-06T09:56:00.244-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='datetime'/><category scheme='http://www.blogger.com/atom/ns#' term='temporal'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Groovy Temporal Support Continued</title><content type='html'>One area where ruby outshines groovy is in date and date time support.  Ruby's date time support isn't more capable, just easier to use.   Java, and groovy have Calendar--very complete, and very clunky.  There is also joda time.  Very complete, but a bit of an overkill for my tastes.&lt;br /&gt;&lt;br /&gt;So here are the objectives:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ability to add/subtract dates with integers representing days, months, etc.&lt;/li&gt;&lt;li&gt;ability to subtract two dates to yield intervals representing days, hours, etc.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ability to compare two dates&lt;/li&gt;&lt;li&gt;ability to parse any type of date input, and most easily parse the standards&lt;/li&gt;&lt;li&gt;ability to format dates in a wide variety of ways&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ability to create dates with integers and words like 10.days.ago or nextweek&lt;/li&gt;&lt;/ul&gt;Java's calendar class supports the first three requirements, parsing is available through SimpleDateFormat class.  Groovy's TimeCategory enables some of the most common integer enhancements to create dates based on common temporal works.  So, the pieces are in place, the next step is to wrap this in a groovy way.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;DateTime, the Groovy Calendar&lt;/span&gt;:  The first step is to create an easy (easier) to use, full featured DateTime class.   One approach is to simply extend GregorianCalanedar and add the new methods, mostly getters and setters.  That's were I started but soon discovered a better solution was to contain the calendar object, and use invokeMethod() to pass methods and arguments.  Then, rather than define properties of the DateTime object, define getters and setters in bean style making the variables appear as if they were members.  For example, I define these two methods:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;int getYear() { return calendar.get(Calendar.YEAR) }&lt;br /&gt;void setYear(int yr) { calendar.set(Calendar.YEAR, yr) }&lt;br /&gt;&lt;/pre&gt;Now, I can use short hand to access "year" like this:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;def dateTime = new DateTime(year:2008)&lt;br /&gt;assert dateTime.year == 2008&lt;br /&gt;dateTime.year = 2020&lt;br /&gt;assert dateTime.year == 2020&lt;br /&gt;dateTime.year++&lt;br /&gt;assert dateTime.year == 2021&lt;br /&gt;&lt;/pre&gt;Groovy, right?  And adding other methods like setSeconds(), clearTime(), isWednesday() were easy one-liners to implement.  I also took the liberty of shifting the month to 1..12 rather than 0..11, the calendar default.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Constructors&lt;/span&gt;: This was also very groovy.  After I added the getter/setters, constructing with the virtual member vars was easy.  So the DateTime class can be constructed like this:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;dt = new DateTime(year:2020, month:4, day:1)&lt;br /&gt;dt = new DateTime(hours:0, minutes:0, seconds:0)&lt;br /&gt;dt = new DateTime().clearTime()&lt;br /&gt;dt = new DateTime() + 5&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Date/Time Formatting&lt;/span&gt;: The Rails group enhanced ruby's DateTime.to_s() method by adding formatting like this: to_s(format).  This makes a lot of sense.  To implement this I use a format hash that stores formatting strings by name.  The map is static to enable use application wide (I  may regret this later) and there is a default format member variable that controls how toString() is formatted.  I also added toString(format) to display a date in multiple formats like this:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;// default format is a modified ISO-8601 called 'db'&lt;br /&gt;dt = new DateTime(year:2010, day:25, month:4, hours:15, minutes:35, seconds:29)&lt;br /&gt;assert dt.toString() == '2010-04-25 15:35:29'&lt;br /&gt;assert dt.toString(8601) == '2010-04-25T15:35:29-0800'&lt;br /&gt;assert dt.toString('mdy') == '04/25/2010'&lt;br /&gt;assert dt.toString('dmy') == '25.04.2010'&lt;br /&gt;assert dt.toString('dMony') == '25-Apr-2010'&lt;br /&gt;&lt;/pre&gt;Other formats are available in the formats hash.  The actual formatting is done using SimpleDateFormat that supplies a wide range of date formats and is compatible with Java 1.4, important in the groovy community.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;A Groovy Date Parser&lt;/span&gt;: I've seen many approaches to this, usually making use of java's SimpleDateFormat class with a boat load of formats.  I think there may be a better way.  First, extracting the date parts, usually the numbers and constructing date time objects based on the numeric values.  I'll discuss this implementation later...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-2900207716232448649?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/2900207716232448649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=2900207716232448649' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2900207716232448649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2900207716232448649'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/03/groovy-temporal-support-continued.html' title='Groovy Temporal Support Continued'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-7560292784410962355</id><published>2008-03-02T11:08:00.000-08:00</published><updated>2008-03-04T11:54:25.641-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='datetime'/><category scheme='http://www.blogger.com/atom/ns#' term='temporal'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Groovy Temporal Support Classes</title><content type='html'>Groovy adds a rich set of features to java including closures, ranges, dynamic metaClass, access to existing java code base, etc.  What is missing is full featured date processing.   One approach to fixing this is to use existing libraries of java date extensions, such as &lt;a href="http://joda-time.sourceforge.net/"&gt;joda time&lt;/a&gt;.  Today I decided to implement some basic date/time features without a third party dependency.  I may end up using joda as the project unfolds, but for now I'm trying to resist the temptation.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;HourMinuteSecond&lt;/span&gt;:  This class acts as a container and formatter of hours, minutes and seconds similar to groovy's &lt;a href="http://groovy.codehaus.org/api/groovy/time/TimeDuration.html"&gt;TimeDuration&lt;/a&gt; class.   In native form it's completely mutable, so not thread safe.  But it can be made immutable by invoking asImmutable().   I'll explain how groovy makes this easy to implement later on.&lt;br /&gt;&lt;br /&gt;The class API is as follows:&lt;br /&gt;&lt;pre style="font-size: 12px; padding-left: 10px;"&gt;static HourMinuteSecond now()&lt;br /&gt;static HourMinuteSecond fromDate(date)&lt;br /&gt;&lt;br /&gt;def parse('hh:mm:ss')&lt;br /&gt;def set(hours, minutes, seconds)&lt;br /&gt;def set(Date dt)&lt;br /&gt;def next()  // enables ++&lt;br /&gt;def previous()  // enables --&lt;br /&gt;def add(hours, minutes, seconds)&lt;br /&gt;def sub(hours, minutes, seconds)&lt;br /&gt;boolean isZero()&lt;br /&gt;String toString() // formatted as hh:mm:ss&lt;br /&gt;void addZeroEventListener(closure)&lt;br /&gt;def asImmutable()&lt;br /&gt;&lt;/pre&gt;The object can be used as a simple up/down timer measured in seconds.  When attached to a one second ticker thread, only the next() method needs to be invoked to tally up the seconds.  Used as a count down timer, an event is triggered when zero is reached.&lt;br /&gt;&lt;br /&gt;The methods are actually closures, so creating the immutable clone was as simple as pointing all the mutators to a single closure that throws an UnsupportedOperationException when a mutating method is called.   This mimics how groovy's List.asImmutable() method works.&lt;br /&gt;&lt;br /&gt;I will post more as the temporal project unfolds, and supply a link once the library is ready for distribution.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-7560292784410962355?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/7560292784410962355/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=7560292784410962355' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7560292784410962355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7560292784410962355'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/03/groovy-temporal-support-classes.html' title='Groovy Temporal Support Classes'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-7979366776285395654</id><published>2008-02-07T21:02:00.000-08:00</published><updated>2008-02-07T21:38:22.076-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='datasets'/><category scheme='http://www.blogger.com/atom/ns#' term='migrations'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><title type='text'>Grails Dataset Loaders, Part 2</title><content type='html'>About a month ago I posted a few thoughts on &lt;a href="http://raincitysoftware.blogspot.com/2008/01/grails-dataset-loaders-aka-test.html"&gt;Dataset Loaders&lt;/a&gt; for Grails.  Now that one of my Grails projects is going into beta, I thought it would be a good time to revisit the issue.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Rails History&lt;/span&gt;: With rails I used fixtures for test data and had to hand code data loaders for production.  I put the loaders in lib/tasks. They were rake tasks that did the initial loading and/or refreshing of data for a specified environment.  So data loading for a production or development was a manual task.  Not a huge burden, but just one more thing to remember.&lt;br /&gt;&lt;br /&gt;Now that I'm in the Grails realm I have created automatic loaders that are environment aware.  A &lt;span style="font-weight:bold;"&gt;DatasetBootStrap&lt;/span&gt; class loads data for development, test, and production whenever the server starts.  This is in line with what hibernate does when it starts up--verify that the domain models match the current data source, and make changes where necessary.  In rails this is done with migrations--a good idea, but yet another manual step.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;DatasetBootStrap&lt;/span&gt;: The DatasetBootStrap class first determines the current environment, then loads the appropriate dataset classes and invokes the "load" method (or closure if you will).  A list of datasets is configured manually for each environment.  This approach makes it possible to share loaders for all three environments (or four if you use staging).  Some data is just for test.  Other data, like a list of US States, is loaded for all three environments.  The main point is nothing is repeated, keeping my code as DRY as possible.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Datasets&lt;/span&gt;: I currently keep my datasets in src/groovy/datasets, but I think that it's time to move this folder into grails-app/conf/datasets.  I also use a package declaration (package datasets) to insure that dataset classes don't conflict with other classes (I've had problems with this in the test structure when trying to create unit tests that have a name conflict with integration tests).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Simulating Rails-Like Test Fixtures&lt;/span&gt;: This was the easy part.  In each test that requires a specific dataset, the setUp() method is used to load as many datasets as necessary to create a solid test environment.  Loading all the data is as easy as calling the DatasetBootStrap's init() method.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Grails Integration&lt;/span&gt;: My goal for Datasets is to propose it's use for all grails projects by integrating it into the grails core.  I have a bit of refactoring to do before this is practical, but would welcome any comments or suggestions from other grails (or rails) users as to what features a Dataset loader module should include.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-7979366776285395654?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/7979366776285395654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=7979366776285395654' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7979366776285395654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7979366776285395654'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/02/grails-dataset-loaders-part-2.html' title='Grails Dataset Loaders, Part 2'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-2316862187635703828</id><published>2008-02-06T19:57:00.001-08:00</published><updated>2008-03-20T15:44:01.139-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rest'/><category scheme='http://www.blogger.com/atom/ns#' term='soap'/><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>UPS Web Services Interface</title><content type='html'>One of my customers has a requirement to use United Parcel Service to ship large and small packages.  UPS has a great set of documentation, but they handle large and small packages with different technologies.  Small packages are simple XML request/response transactions that provide methods for requesting a shipment, accepting shipments, and voiding shipments.  Large packages allow you to request a shipment, but the request/response is all in SOAP, something of an overkill in my opinion.&lt;br /&gt;&lt;br /&gt;UPS requires lots of data concerning who, what and where to establish their pricing.  Data is sent to a URL/end point and a response is returned.  The response includes pricing and tracking numbers, as well as image data for printing barcoded shipping labels.   So, it is mainly a data transfer operation-- a single service.&lt;br /&gt;&lt;br /&gt;SOAP is great if you have lots of defined services, like weather in Seattle or Paris or zip code 94705.  Or conversion of units from/to.  But for simple data transfer, REST, or just plain old XML request/response is much more efficient.&lt;br /&gt;&lt;br /&gt;Here is a diagram of the complete data transfer...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;As you can see, there are lots of communications.  The first question is, why is there a server in the middle?  Well, here's the deal--the SunReturns client is just that, a client.  The data really resides at the DataServer--the client simply persists it's own private session.  The UPS server is at their site. All communications are via SOA/services--some simple request/response (ala REST) and others use SOAP.&lt;br /&gt;&lt;br /&gt;The technology used in the middle is grails/groovy.  The SunReturns server is currently a mixture of JSP (legacy) and groovy/GSP.  The groovy components are XML only and include controllers, services, domain objects, etc--similar to grails but without the Hibernate layer.  It talks to legacy databases, both Oracle and MySQL--not impossible with grails, but definitely outside the conventions. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Conclusions:&lt;/span&gt; The natural loose coupling of SOA provides a new vehicle to solve business problems.   At the same time, distributing business processes across diverse servers provides a workable migration path to newer technologies.  Groovy, grails, and simple XML transport provide a way to implement SOA with a minimum of effort.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-2316862187635703828?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/2316862187635703828/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=2316862187635703828' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2316862187635703828'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2316862187635703828'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/02/ups-web-services-interface.html' title='UPS Web Services Interface'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-3556952526730200725</id><published>2008-01-16T17:36:00.000-08:00</published><updated>2008-01-16T18:00:51.231-08:00</updated><title type='text'>Business Service Components</title><content type='html'>Every business application has a set of commonalities that lend themselves to standardization.  In the rails, and to some extent grails world, this translates into &lt;a href="http://agilewebdevelopment.com/plugins/"&gt;plugins&lt;/a&gt;.  The restful authorization plugin being a good example.&lt;br /&gt;&lt;br /&gt;But, what if rather than a plugin, you had access to a software service that provide the same functionality?  Here is a comparison:&lt;br /&gt;&lt;br /&gt;Using a business related plugin:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;download and install the plugin into your project&lt;/li&gt;&lt;li&gt;build out all the required tables&lt;/li&gt;&lt;li&gt;hookup your internal logic to insure that the plugin loads correctly&lt;/li&gt;&lt;li&gt;use the plugin&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Using a Software Business Service:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;use the service&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;The choice should be obvious.  Rather than dealing with database migrations, version control, broken or non-existent tests, integration into your pristine project, you simply use the service.  A good example is the &lt;a href="http://www.raincitysoftware.com/captcha.php"&gt;captcha&lt;/a&gt; service called &lt;a href="http://captchator.com/"&gt;Captchator&lt;/a&gt; from &lt;a href="http://shop.mikrocontroller.net/shop_impress.php"&gt;Andreas Schwartz&lt;/a&gt;.  This service replaces logic that traditionally comes in the form of a plugin with a callable service that does what a captcha component needs to do.  No downloads required.  No migration of new tables.  Just use the service.&lt;br /&gt;&lt;br /&gt;Now imagine a full suite of services for business processes.  Authentication, company and contact information, time tracking, invoicing, fulfillment--all discrete services that a VAR or software developer can simple use as components in a vertical solution.   Wouldn't that be cleaner than installing plugins?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-3556952526730200725?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/3556952526730200725/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=3556952526730200725' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3556952526730200725'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3556952526730200725'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/01/business-service-components.html' title='Business Service Components'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-667098251188458084</id><published>2008-01-05T09:12:00.001-08:00</published><updated>2008-01-05T09:38:15.601-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git backup database sql'/><title type='text'>Git for distributed backups</title><content type='html'>I usually use svn for code and document backups but today I decided to use git to automate my database backups.  Git seems to run much faster and offers a bit more security.  It also functions as a good transport for multiple distribution to remote sites.&lt;br /&gt;&lt;br /&gt;The backups are in ~/backups so I created a git repository and moved it to my remote slicehost site.  Here are the steps:&lt;br /&gt;&lt;ul style="list-style:none;font-size:12px;font-family:courier;"&gt;&lt;li&gt;cd backups&lt;/li&gt;&lt;li&gt;git init&lt;/li&gt;&lt;li&gt;git commit -a -m 'initial repo'&lt;/li&gt;&lt;li&gt;cd ~&lt;/li&gt;&lt;li&gt;git clone --bare ~/backups backups.git&lt;br /&gt;&lt;/li&gt;&lt;li&gt;scp -r backups.git dpw@raincity.slice.com:&lt;/li&gt;&lt;/ul&gt;The next step was to move the backups.git folder to /public/git and chown -R git:git.  Then to read the repo back:&lt;br /&gt;&lt;ul style="list-style:none;font-size:12px;font-family:courier;"&gt;&lt;li&gt;mv backups /tmp&lt;/li&gt;&lt;li&gt;git clone ssh://git@raincity.slice.com/public/git/backups.git&lt;/li&gt;&lt;/ul&gt;At this point I have local and remote repos.  I went to other machines and cloned the repo to insure that I have multiple locations on and off site.  The next step was to hook git up to my standard backup from the master database.  At the tail end of the SQL backup script I do this:&lt;br /&gt;&lt;ul style="list-style:none;font-size:12px;font-family:courier;"&gt;&lt;li&gt;git add .&lt;/li&gt;&lt;li&gt;git commit -m "automated backup on `date`..."&lt;/li&gt;&lt;li&gt;git push&lt;/li&gt;&lt;/ul&gt;And there you go.   This provides a local and remote backup.   Configuring cron on other machines is as simple as doing a 'cd ~/backups ; git pull'.   Life is easy...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-667098251188458084?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/667098251188458084/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=667098251188458084' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/667098251188458084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/667098251188458084'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/01/git-for-distributed-backups.html' title='Git for distributed backups'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-1539522830225726198</id><published>2008-01-05T06:45:00.000-08:00</published><updated>2008-01-16T08:23:26.697-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='gorm'/><category scheme='http://www.blogger.com/atom/ns#' term='database design'/><category scheme='http://www.blogger.com/atom/ns#' term='datasets'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='fixtures'/><title type='text'>Survey Project--A Grails Implementation</title><content type='html'>I started small grails application today to enable creating surveys and questions and tracking questions and results. The domain classes are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Survey: the primary table that defines a survey project&lt;/li&gt;&lt;li&gt;SurveyQuestion: object to hold a specific question mapped to a survey&lt;/li&gt;&lt;li&gt;SurveyResponder: the person that is answering the survey questions&lt;/li&gt;&lt;li&gt;SurveyResponse: the recorded answers that the reponder enters&lt;/li&gt;&lt;/ul&gt;And here is the proposed ER diagram, created using dbDesigner.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://raincitysoftware.com/docs/SurveyService.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px;" src="http://raincitysoftware.com/docs/SurveyService.png" alt="" border="0" /&gt;&lt;/a&gt;As the diagram shows, surveys have many questions, questions have many responses.  Surveys also have many responders and responders have many responses.   You can also see that this is a very simple survey with no branches.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Grails/GORM Implementation&lt;/span&gt;:&lt;br /&gt;I began by generating the Survey and Survey question domain classes and basic validation tests.  Next was to create the SurveyReponder and SurveyResponse classes.   The implementation was straight forward, but I had to fiddle with the domain class definitions to get hibernate to understand my intent.  Just a matter of specifically defining class references in dependent models rather than depending on the 'belongs_to' and 'has_many' declarations.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Datasets and Loaders (Fixtures)&lt;/span&gt;:&lt;br /&gt;Unlike Rails, Grails doesn't use fixtures for test data.   Lucky for me I have a Grails enhancement the solves this based on a &lt;a href="http://grails.codehaus.org/Test+data+fixtures"&gt;sandbox proposal&lt;/a&gt; from the Grails guys.  So I generated the dataset loader classes using: grails create-dataset [domain].  This creates the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;a DataLoaderBootStrap class in grails-app/conf/ (unless it exists)&lt;/li&gt;&lt;li&gt;a DomainDataset class in src/groovy/datasets&lt;/li&gt;&lt;li&gt;a set of random  data to be loaded&lt;/li&gt;&lt;/ul&gt;An example of what gets created is here:&lt;br /&gt;&lt;div style="font-size: 85%; font-family: courier new;"&gt;&lt;br /&gt;&gt; grails create-dataset survey&lt;br /&gt;&gt; ...&lt;br /&gt;&gt; file grails-app/conf/DataLoaderBootStrap.groovy exists...&lt;br /&gt;&gt; created file src/groovy/datasets/SurveyDataset.groovy&lt;br /&gt;&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The SurveyDataset class looks like this:&lt;br /&gt;&lt;div style="font-size: 85%; font-family: courier new;"&gt;&lt;pre&gt;&lt;br /&gt;class SurveyDataset {&lt;br /&gt;dataset = {&lt;br /&gt;def set = [&lt;br /&gt;[ name:'879',description:'510',email:'680' ],&lt;br /&gt;]&lt;br /&gt;&lt;br /&gt;return set&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;load() {&lt;br /&gt;dataset().each { data -&gt;&lt;br /&gt;obj = new Survey( data )&lt;br /&gt;obj.save()&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;As you can see, the data is contrived but all of the fields are present and other than the actual data, the script is good to go.   I'll show how this data can be loaded from the console, command line and within test scripts in a later post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-1539522830225726198?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/1539522830225726198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=1539522830225726198' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/1539522830225726198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/1539522830225726198'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/01/survey-project-grails-implementation.html' title='Survey Project--A Grails Implementation'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-7639189500292927841</id><published>2008-01-04T18:30:00.000-08:00</published><updated>2008-01-04T19:01:24.051-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='grails groovy gorm fixtures rails java closures'/><title type='text'>Grails Dataset Loaders, aka Test Fixtures</title><content type='html'>One thing I've been missing since working in Grails is test fixtures ala Rails.  The first steps I follow when developing data models is to create test data, which is fairly easy in Rails.  For Rails I had to do some extra work to get this data to load into the development db and I also  had to augment the yaml files with ERB to generate random data.&lt;br /&gt;&lt;br /&gt;The Grails sandbox area has a &lt;a href="http://grails.codehaus.org/Test+data+fixtures"&gt;thread&lt;/a&gt; that proposes creating test fixtures that begin in late 2006.  As the conversation unfolded, there was a decision to use the term Dataset rather then Fixture--a welcomed change.  But the thread kind of died as of October of 2006, so I guess I need to implement this on my own.  Here's my attempt...&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;Datasets and Loaders (Fixtures)&lt;/span&gt;:&lt;br /&gt;I followed the sandbox proposal and created Dataset classes rather than yaml files.  Closures were an obvious choice to create datasets, but I also had some other requirements. My objectives were:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;datasets are usually a single set, but may be separated by dev, test, and production&lt;/li&gt;&lt;li&gt;datasets are implemented as closures to enable generating random data&lt;br /&gt;&lt;/li&gt;&lt;li&gt;dataset loaders may be separated into development, test, and production&lt;/li&gt;&lt;li&gt;they can be loaded from within the application on start up&lt;/li&gt;&lt;li&gt;they can be loaded on demand from test classes (similar to calling out fixtures)&lt;/li&gt;&lt;li&gt;they can be loaded through scripts (ant or grails)&lt;/li&gt;&lt;li&gt;they can be loaded from grails shell and console&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;For all environments a DataLoaderBootStrap class controls loading when the application starts (grails run-app).   The bootstrap is environment aware, and loads specified datasets from individual Dataset classes, e.g. UserDataset, CountryDataset, etc.  based on the current environment. &lt;br /&gt;&lt;br /&gt;The datasets are classes that have a dataset closure that defines hash maps of data (not model classes) and a load() method to do the actual database inserts (or updates).   The classes are placed in the src/groovy/datasets folder.  Their is a load() method that creates objects from the hash set then invokes "save" to either insert or update depending on the state of the database. This has the benefit of creating predictable data without having to continually drop or trucate tables (ala rails).&lt;br /&gt;&lt;br /&gt;When testing in the console, the loaders can be run manually.   Test scripts can invoke loaders at any time to insure that there is predictable data.   A command line script can be invoked to load all data for the given environment using "grails [env] load-data".&lt;br /&gt;&lt;br /&gt;I thought of creating a plugin for this but it doesn't really fit the plugin framework.  So, for now  I plan to simply keep it as a platform enhancement.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-7639189500292927841?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/7639189500292927841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=7639189500292927841' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7639189500292927841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7639189500292927841'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/01/grails-dataset-loaders-aka-test.html' title='Grails Dataset Loaders, aka Test Fixtures'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-8989559562629371389</id><published>2008-01-02T09:14:00.000-08:00</published><updated>2008-01-02T09:40:44.150-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='resin'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jetty'/><category scheme='http://www.blogger.com/atom/ns#' term='virtual servers'/><category scheme='http://www.blogger.com/atom/ns#' term='slicehost'/><title type='text'>Web Containers on SliceHost</title><content type='html'>I have a small virtual server at slice host (256M).  I originally thought it would be appropriate for rails/mongrel and apache, but probably too small for a java web container.  But, after installing resin and running it through some basic tests, it looks like even the small slice is fine for testing and semi-live (e.g. demo) applications.&lt;br /&gt;&lt;br /&gt;My next step is to test jetty and glassfish.  I don't think there will be problems with jetty, but glassfish may be too much of a memory hog to work correctly.&lt;br /&gt;&lt;br /&gt;Running 'top'  shows that resin actually uses about 60M when it starts up and goes up to 90M under basic use.  This will undoubtedly  change when spring/hibernate and real database access kicks in, but it looks good so far.   My goal is to install a grails application to see if slice host can be used for basic demo purposes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-8989559562629371389?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/8989559562629371389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=8989559562629371389' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/8989559562629371389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/8989559562629371389'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2008/01/web-containers-on-slicehost.html' title='Web Containers on SliceHost'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-3263819681228646244</id><published>2007-12-30T18:30:00.000-08:00</published><updated>2008-01-02T09:39:21.818-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='gorm'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='active record'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>10 Reasons to Switch from Rails to Grails</title><content type='html'>After spending a few years really enjoying Rails it was difficult to bring myself to even try groovy and grails.  But my latest contract forced me to look for alternatives, and I'm glad I did.  Here are some reasons that you may want to switch...&lt;br /&gt;&lt;ol&gt;&lt;li&gt;GORM with hibernate/spring and jpa is much better than ActiveRecord&lt;/li&gt;&lt;li&gt;No distribution problems; runs in many production ready containers&lt;/li&gt;&lt;li&gt;Internationalization out of the box (not specifically ignored as DHH does)&lt;/li&gt;&lt;li&gt;Transactions actually work, and they include save-points.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Not only dynamic finders and counters, but dynamic listOrderBy&lt;br /&gt;&lt;/li&gt;&lt;li&gt;No green threads (although this may be fixed in Ruby 2.0, around 2010?)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Ability to use pessimistic locking out of the box&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Real-live prepared statements and .withCriteria method&lt;/li&gt;&lt;li&gt;Production level test reporting with built in Mocking and Stubbing&lt;/li&gt;&lt;li&gt;Search operations are based on Lucene (with a plugin)&lt;/li&gt;&lt;/ol&gt;All of these don't make sense for a non-java coder.  And my startup time for grails would have be much longer without my prior experience with Rails and Ruby.&lt;br /&gt;&lt;br /&gt;One thing I thought I might be giving up was the endless list of valuable gems that make developing in Rails a real  pleasure.   But after re-reviewing the list of open source products from apache, java source, spring, hibernate, and even Sun (glassfish), I can't think of any gems that I will miss.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-3263819681228646244?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/3263819681228646244/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=3263819681228646244' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3263819681228646244'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/3263819681228646244'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/12/10-reasons-to-switch-from-rails-to.html' title='10 Reasons to Switch from Rails to Grails'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-1423891133980199847</id><published>2007-12-30T16:10:00.000-08:00</published><updated>2008-01-02T09:37:18.948-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='grails groovy gorm java'/><title type='text'>Grails Application Development Notes</title><content type='html'>I started a grails application for Sun using the latest version of groovy (1.5) and grails (1.0-RC3).  Things went fairly smooth but I was glad to be under 'git' control to rollback branches as the project developed.  My first problem with with mixed case characters in domain and controllers...&lt;br /&gt;&lt;br /&gt;I originally created an IBBUser domain model.  Ok, I should probably re-name it IbbUser but old habits die hard.  Anyway, the model generation went fine, but when I went to create the controller using generate-all, that's when things fell apart.&lt;br /&gt;&lt;br /&gt;I'm not sure why (yet) but 'generate-all' created my views in an IBBUser folder.  This was a problem when I ran the app, so I scrapped the project and re-generated with the domain name of 'user'.  I probably could have used 'ibbuser' in lower case, but oh well...&lt;br /&gt;&lt;br /&gt;Working with generated code in grails or rails can get out of hand quickly--so many files and folders to track.  So I set up branches--one for domains, the other for controllers.   Before generating code, I  quickly go to the appropriate branch, generate and test the code, do multiple commits then merge it to the master branch when I'm sure things are working as they should.  If not, it's easy to revert or even delete the entire branch and start over.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Database Configuration&lt;/span&gt;:  Out of the box grails configures the application for hypersonic.  To configure MySQL requires three steps: 1) create the development, test, and production databases and login permissions, 2) modify the conf/DataSource.groovy configuration, and 3) drop the MySQL connector/j jar into the project's lib folder.  When this is complete simply do a 'grails run-app' and you will see the new tables appear in the database.&lt;br /&gt;&lt;br /&gt;The project at this point has only a single domain class, User so a single table is created called 'user'.  All columns were created including an 'id' and 'version' column.  Without any constraints, all columns default to 'not null'.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;I prefer the rails convention of plurals and lock_version, but I'll leave that for now.   &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Constraints&lt;/span&gt;: Adding constraints was very straight forward.  I watched the server restart and the database re-build with each change.   Lots of magic going on in the background.  I tested the constraints through the UI.  The next step is to create unit and integration tests.  More on that in a later post...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-1423891133980199847?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/1423891133980199847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=1423891133980199847' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/1423891133980199847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/1423891133980199847'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/12/grails-application-development-notes.html' title='Grails Application Development Notes'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-4325762796318945022</id><published>2007-12-11T11:40:00.000-08:00</published><updated>2007-12-11T16:17:11.644-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java groovy'/><title type='text'>Upgrading to Groovy 1.5</title><content type='html'>I upgraded to the latest 1.5.0 version of &lt;a href="http://groovy.codehaus.org/"&gt;groovy&lt;/a&gt; today.   I was a bit of a pain, but after writing an install script (sandbox/groovy/install-groovy.sh) I was able to update all of my servers.  Some of the new features include:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;enums&lt;/li&gt;&lt;li&gt;annotations&lt;/li&gt;&lt;li&gt;generics&lt;/li&gt;&lt;li&gt;groovy interactive shell&lt;br /&gt;&lt;/li&gt;&lt;li&gt;updates to the groovy console&lt;/li&gt;&lt;/ul&gt;You can read about additional upgrade specifics and view code snippet examples &lt;a href="http://www.infoq.com/articles/groovy-1.5-new"&gt;here&lt;/a&gt;.   Quoting from the article...&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-style: italic;"&gt;Groovy is Java, and Groovy makes Java groovier. Compared with other languages, Groovy is certainly the language that provides the &lt;/span&gt;&lt;strong style="font-style: italic;"&gt;flattest learning curve to Java developers&lt;/strong&gt;&lt;span style="font-style: italic;"&gt;, thanks to a very similar syntax.&lt;/span&gt;&lt;/blockquote&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;I would add that ex-java programmers that have moved to ruby will love groovy.  Closures, interactive shell, access to all your current java libraries,  built in ORM, and lots of other goodies.&lt;br /&gt;&lt;br /&gt;Here is a sample of how I use groovy to read job logs.  Jobs run nightly on Sun production servers...&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;import groovy.sql.Sql&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;import database.*&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;oracle = new Oracle()&lt;/span&gt;  &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;today = oracle.timestamp(new Date() - 1)&lt;/span&gt; &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;db = oracle.getStarsProd()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;sql = "select * from job_log where start_time &gt; ? order by job_id"&lt;/span&gt;  &lt;span style="font-family:courier new;"&gt;db.eachRow(sql, [ today ]) {job -&gt;&lt;/span&gt; &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;    print "$job.job_id $job.start_time &lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;$job.end_time&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;"&lt;br /&gt;    println "\t$job.row_count\t$job.error_count\t$job.job_name"&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;This simple script dumps jobs run in the current day.  The database package is a collection of groovy scripts that contain oracle specific connections and utilities.  More on the specifics of what's in that package in another post...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-4325762796318945022?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/4325762796318945022/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=4325762796318945022' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/4325762796318945022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/4325762796318945022'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/12/upgrading-to-groovy-15.html' title='Upgrading to Groovy 1.5'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-5264379630191602581</id><published>2007-12-10T11:23:00.000-08:00</published><updated>2007-12-11T09:43:33.777-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flex adobe git'/><title type='text'>Git for Flex Projects</title><content type='html'>I have been using subversion for Flex2 projects since it was released in 2006.  I use the full version because I had problems getting the eclipse plugin to work.  But I had other problems with the full Flex2 IDE as well.  One problem was there is no facility to exclude files from the build path.  So, all of my .svn files get copied from the src folder to bin.  Permission problems also don't allow me to do a "clean" through the Flex2 IDE.  Bad.  Very bad.&lt;br /&gt;&lt;br /&gt;So, I have converted all my Flex2 and action script 3 projects to use git rather than subversion.  Simple to do, I just exported my adobe subversion branch and ran git init.   I added a single .gitignore at the project root to ignore files in all the bin folders.  Then I used Flex2 to recompile all the projects and ran git status to verify the ignores.  It's all good...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Note: this would have been a bit easier to just use git-svn clone but my mac is missing the perl bindings required for git-svn.&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;The Git Server&lt;/span&gt;&lt;span&gt;:  Next was to create a --bare copy and transfer it to my git server at slicehost.  First the clone.&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;git clone --bare ~/work/adobe adobe.git&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;touch adobe.git/git-daemon-export-ok&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;scp -r adobe.git dpw@raincity.slice.com:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;Then on the server, simply move this to the git directory and fix the permissions.&lt;br /&gt;&lt;blockquote  style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;cd /public/git/&lt;br /&gt;sudo mv ~/adobe.git .&lt;br /&gt;sudo chown -R git:git adobe.git&lt;/span&gt;&lt;/blockquote&gt;And test on the client side with the following &lt;/span&gt;&lt;span&gt;and the new project is loaded to the client.&lt;/span&gt;&lt;span&gt;&lt;span style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;&lt;blockquote&gt;git clone ssh://git@raincity.slice.com/public/git/adobe.git adobe &lt;/blockquote&gt;&lt;/span&gt;After getting used to git I plan to move all projects there.  If I can find the perl hooks, I'll probably use git-svn at least the the older (Sun) projects.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-5264379630191602581?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/5264379630191602581/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=5264379630191602581' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/5264379630191602581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/5264379630191602581'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/12/git-for-flex-projects.html' title='Git for Flex Projects'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-650047996460265841</id><published>2007-12-08T09:00:00.000-08:00</published><updated>2007-12-08T11:17:33.018-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails rails2.0 update-problems actionmailer rubyforge gems'/><title type='text'>Rails 2.0</title><content type='html'>I spent some time today to convert all my on-going projects to Rails 2.0.  Not a huge effort, but here are my notes.&lt;br /&gt;&lt;br /&gt;My first problem was getting &lt;span style="font-style: italic;"&gt;"gem install rails"&lt;/span&gt; to upgrade smoothly.  It appears that the rails team noticed the problem and quickly put up version 2.0.1, but it also failed.  A quick google search pointed me to a &lt;a href="http://blog.zabiello.com/articles/2007/12/07/rails201"&gt;Polish&lt;/a&gt; site that had what looked like a solution.   A few modifications  and I was ready to go.   The highlights are...&lt;br /&gt;&lt;pre&gt;&lt;code class="typocode_bash"&gt;svn co http://dev.rubyonrails.org/svn/rails/tags/rel_2-0-1/ rails-2.0.1&lt;br /&gt;&lt;br /&gt;cd rails-2.0.1&lt;br /&gt;&lt;br /&gt;for f in *&lt;br /&gt;do&lt;br /&gt; [ -d $f/pkg ] &amp;amp;&amp;amp; { cd $f ; rake package ; cd .. }&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;for f in */pkg/*.gem&lt;br /&gt;&lt;br /&gt;do&lt;br /&gt; sudo gem install $f --local&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;With the six gems now freshly compiled I put them into a tarball and moved them to a distribution point.  Then I wrote a short script to untar and install locally (sandbox/rails/gem-install.sh).  That made it easy to update all of my machines, including slicehost.  &lt;span style="font-style: italic;"&gt;(I did have a problem on one of my older machines.   I had to modify the script to load the gems one at a time).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next were the tests.  I was able to get all projects to test without errors.  I created a small demo project to make sure that was working as well.&lt;br /&gt;&lt;br /&gt;Happy rails 2.0.1!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-650047996460265841?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/650047996460265841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=650047996460265841' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/650047996460265841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/650047996460265841'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/12/rails-20.html' title='Rails 2.0'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-6188859287121738042</id><published>2007-12-07T08:07:00.000-08:00</published><updated>2007-12-07T08:46:17.965-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git subversion'/><title type='text'>Replacing Subversion with Git for version control</title><content type='html'>I downloaded git on one of my fedora machines (fedora 7) to get a feeling for how it works vs subversion.  I used yum to get the latest version (1.5.3.3) with this command:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;blockquote&gt;sudo yum  install git&lt;/blockquote&gt;&lt;/span&gt;Before getting started with git, first thing is to establish some global configurations.  The standard configs are saved in ~/.gitconfig (currently in config/common).    There is also a ~/.gitk config file that should be modified for the mac (put in config/osx/sedona) to set the font to Monaco 12 rather than Helvetica 9.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Using Git&lt;/span&gt;:   There are &lt;a href="http://git.or.cz/"&gt;tutorials&lt;/a&gt; for using git on the main site and one for subversion users.  After the installation, I used the tutorial to establish tracking to some basic accounting files, time sheets, check register, etc.  It started like this.&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;cd accounting&lt;br /&gt;git init&lt;br /&gt;git add .&lt;br /&gt;git commit&lt;/blockquote&gt;Now the accounting directory is under source control.    Another great resource for git is from &lt;a href="http://peepcode.com/"&gt;peepcode&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Remote Repository&lt;/span&gt;:  The next step is to get a remote repository  established.  I played around with the git socket protocol on slice.host, but found it a bit too confusing.  So after reviewing the peepcode git session, I decided to use ssh and a restricted shell for the remote repository.  I followed these steps:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;login to the remote machine (raincity.slice.com)&lt;/li&gt;&lt;li&gt;create a new 'git' user: useradd --shell /usr/bin/git-shell git&lt;/li&gt;&lt;li&gt;copy to the server: scp -r accounting dpw@raincity.slice.com:accounting&lt;br /&gt;&lt;/li&gt;&lt;li&gt;create a /public/git folder and move the accounting to it&lt;/li&gt;&lt;li&gt;set owner to git:git&lt;/li&gt;&lt;/ul&gt;So it wasn't too difficult.    After running clone, I did a few push and pulls from different machines and watched the changes on the remote repository.  Works good...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-6188859287121738042?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/6188859287121738042/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=6188859287121738042' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/6188859287121738042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/6188859287121738042'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/12/replacing-subversion-with-git-for.html' title='Replacing Subversion with Git for version control'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-7560164286383466626</id><published>2007-12-05T10:00:00.000-08:00</published><updated>2007-12-05T13:17:37.413-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails flex sprouts'/><title type='text'>Sprouts for Flex Application Generation</title><content type='html'>A second generation refactoring of AsProject for creating Flex projects is being created by Luke Bayes and Ali Mills at &lt;a href="http://www.asserttrue.com/"&gt;assertTrue&lt;/a&gt;.  The project has great promise, and since it is based on ruby, gems, yaml and rake it has instant credibility with me.    The project is in alpha stage, so there is lots of work to be completed, but here are some thoughts about my first experiences with sprouts...&lt;br /&gt;&lt;br /&gt;First, I installed the gem on both OSX (10.4) and Fedora (7) with:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;sudo gem install sprout&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Then I created my first project on both machines.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;sprout -s mxml MyProject&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Since this is the first application in this project, there are a bunch of dependency files that need to be downloaded.  Sprout gives a good visual indication of what it's doing, so it was not a problem.    Once the basic projects were created, it was time for the initial test.&lt;br /&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;cd MyProject/project ;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt; rake&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If you don't have a mm.cfg at your home directory you'll be prompted (pestered?) to create one.  This is more of a flex thing rather than sprouts, and I have lots of issues with how flex handles the debug thing, but for now, I just answered yes...&lt;br /&gt;&lt;br /&gt;My results on linux were as expected.   The project compiled and ran just fine.   No so on my mac.   It compiled ok, but the runner failed.    I was able to access the compiled swf on my browser so I'm assuming that my mac's flex installation is messing up the runner.  (After perusing the &lt;a href="http://code.google.com/p/projectsprouts/issues/list"&gt;project issues&lt;/a&gt; I see that this is a known problem.   I also looked at a post from &lt;a href="http://web.mac.com/mnem/Leaky_Mnembrain/Setting_up_Sprouts_on_OS_X.html"&gt;LeakyMmembrain&lt;/a&gt; to help smooth out some issues (but rake still hangs)...)&lt;br /&gt;&lt;br /&gt;So now that I have a project framework, next is to see how it works with the Flex/Eclipse IDE  (flex 2 on my mac only).   This wasn't as I hoped.  The generated structure doesn't play nice with Flex's vision of projects, i.e., there is a single level project where src, test, assets, css, and lib reside.  but, on the same level are script, rakefile.rb and bin.  What flex tries to do is grab all the files and copy them to the bin folder--not good.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Sprout Wish List&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;Here is a short list of things I would like to see in sprouts:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;a rake plugin similar to 'lib/tasks' in rails&lt;/li&gt;&lt;li&gt;rake file named Rakefile (ok, that's a nit)&lt;/li&gt;&lt;li&gt;better Flex IDE integration, at least for mxml projects&lt;/li&gt;&lt;/ul&gt;So, I'm hoping that future versions, or possibly a custom template configuration will solve the Flex IDE problem.   In any case I'm looking forward to following the progress of the sprouts.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-7560164286383466626?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/7560164286383466626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=7560164286383466626' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7560164286383466626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7560164286383466626'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/12/sprouts-for-flex-application-generation.html' title='Sprouts for Flex Application Generation'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-8633667669823358858</id><published>2007-12-05T09:30:00.000-08:00</published><updated>2007-12-06T17:57:42.349-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haxe flex adobe'/><title type='text'>Programming for the Haxe Compiler</title><content type='html'>Haxe is a programming environment and compiler for javascript and action script like files.  It will compile to flash swf files so it's great for web application development.&lt;br /&gt;&lt;br /&gt;I downloaded and installed the linux version (fedora 7) and tried out a few of the examples.  The first was a classic hello world app.  Simple, fast compiler, but with Flex/Flash now open source, I don't think there is a compelling reason to use haxe.&lt;br /&gt;&lt;br /&gt;But, I left the files in my sandbox.  You never know...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-8633667669823358858?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/8633667669823358858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=8633667669823358858' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/8633667669823358858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/8633667669823358858'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/12/programming-for-haxe-compiler.html' title='Programming for the Haxe Compiler'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-1328089147347395478</id><published>2007-12-03T08:00:00.000-08:00</published><updated>2008-12-11T14:31:05.301-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='google maps kml ruby util geo'/><title type='text'>Geo Coding with Google KML</title><content type='html'>The google map API is easy to use either from javascript or directly through curl, or ruby.  A recent post by &lt;a href="http://developer.assaydepot.com/?p=25"&gt;assay depot&lt;/a&gt; on replacing or foregoing the heavy weight Cartographer plugin for a lighter approach has inspired me to produce a stand-alone ruby library to grab coordinates and address info using google, or any other service (&lt;a href="http://geocoder.us/"&gt;geocoder.us&lt;/a&gt; comes to mind).   So, here's my attempt.&lt;br /&gt;&lt;br /&gt;First off is to get registered with google to get an access key.  Second was to place this in a secure location so that it can be used, but not seen by others.  So I created a simple google.rb script to house this and other login creds.  It's stored in the home directory of what ever server I'm currently on, so easy to get to but out of view from the web space.&lt;br /&gt;&lt;br /&gt;The next step was to create a simple value object that holds physical address information.  I tried to make the object as generic as possible by cross referencing google KML (keyhole markeup language) schema and the commercial geocoder.us attributes as well as the TIGER/Line data provided by the US Census Bureau . Lots of attributes, but what I need is basic address and coordinates so the attributes are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;street (or thoroughfare)&lt;/li&gt;&lt;li&gt;city&lt;/li&gt;&lt;li&gt;state code&lt;/li&gt;&lt;li&gt;postal code&lt;/li&gt;&lt;li&gt;country&lt;/li&gt;&lt;li&gt;latitude&lt;/li&gt;&lt;li&gt;longitude&lt;/li&gt;&lt;li&gt;accuracy&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Next was to create a query request object.  The query request is used to launch queries to any geo source, parse the results and return a populated value object.  A typical query would be:&lt;br /&gt;&lt;pre&gt;&lt;span style="font-family:courier new;"&gt;include Geocode&lt;br /&gt;source = GoogleMapSource.new(Google.access_key)&lt;br /&gt;request = Query.new(source)&lt;/span&gt; &lt;span style="font-family:courier new;"&gt;    &lt;/span&gt; &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;info = request.search('1211 13th St, Boulder')&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;The result value object (info) returned from the search would show:&lt;br /&gt;&lt;pre&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;street =&gt; 1211 13th St &lt;/span&gt; &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;city =&gt; &lt;/span&gt;&lt;span style="font-family:courier new;"&gt;Boulder&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;state =&gt; CO&lt;br /&gt;postal_code =&gt; 80302&lt;br /&gt;country =&gt; USA&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;latitude =&gt; 40.008704&lt;/span&gt; &lt;span style="font-family:courier new;"&gt;longitude =&gt; -105.276221&lt;br /&gt;accuracy =&gt; 8&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-size:100%;"&gt;The search results from google is in XML format, so populating the address object with the correct values requires parsing the results.  For this I created a KmlParser class.  The KML schema supports multiple addresses, county, altitude, and other attributes.  Here is the google output response to the previous query.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div style="text-align: left;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_7lr-p1KyMn0/R1NuLlTkUNI/AAAAAAAAABA/caVeyTg3ttc/s1600-R/kml.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 463px; height: 396px;" src="http://4.bp.blogspot.com/_7lr-p1KyMn0/R1NuLlTkUNI/AAAAAAAAABA/pSV_cOpfgM8/s320/kml.png" alt="" id="BLOGGER_PHOTO_ID_5139572745031667922" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:100%;"&gt;The QueryObject returns the first address found, but subsequent addresses are available (query.addresses).  Also the status code for the query is available along with a boolean status_ok method, so a safer call would be:&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;span style="font-family:courier new;"&gt;request.search('1211 13th St, Boulder')&lt;br /&gt;info = request.addresses.first if request.status_ok&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-size:100%;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-size:100%;"&gt;You can download the classes including address info, parser, google access, etc &lt;a href="http://raincitysoftware.com/downloads/geocode-2007.12.06.tar.gz"&gt;here&lt;/a&gt;.  &lt;span style="font-style: italic;"&gt;(Note: You will have to provide your own access code to use.)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-1328089147347395478?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/1328089147347395478/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=1328089147347395478' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/1328089147347395478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/1328089147347395478'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/12/geo-coding-with-google-kml.html' title='Geo Coding with Google KML'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_7lr-p1KyMn0/R1NuLlTkUNI/AAAAAAAAABA/pSV_cOpfgM8/s72-c/kml.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-9144197963230930037</id><published>2007-12-01T16:24:00.000-08:00</published><updated>2008-12-11T14:31:05.893-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='openid rails flex login'/><title type='text'>OpenID with Flex and Rails</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Standard Applications&lt;/span&gt;...&lt;br /&gt;&lt;br /&gt;We actively support OpenID for our open source products that require login authentication.  Most applications don't require rigorous authentication, so presenting a simple dialog with only open id uri works fine.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_7lr-p1KyMn0/R1G7elTkUMI/AAAAAAAAAA4/Jyd4oC1qoh4/s1600-R/access-dialog.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_7lr-p1KyMn0/R1G7elTkUMI/AAAAAAAAAA4/r9Hm4IUK6Pw/s320/access-dialog.gif" alt="" id="BLOGGER_PHOTO_ID_5139094783891099842" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The dialog above allows the user to access an open id application or register for a new open id account.  The registration process takes the user to an open id provider, such as MyOpenID.  The backend, rails in this case uses the access uri to discover the provider and confirm the authentication, i.e., log the user in.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Additional Security&lt;/span&gt;...&lt;br /&gt;&lt;br /&gt;Using open id for your applications means that you don't have to store, or protect the user's password.  It also lets the user access many applications with a single password.   But sometimes an application, or a specific process within an application requires further authentication.  That's where the authentication dialog comes in.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7lr-p1KyMn0/R1G6o1TkULI/AAAAAAAAAAw/XhjL7nSGbIo/s1600-R/authentication-dialog.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_7lr-p1KyMn0/R1G6o1TkULI/AAAAAAAAAAw/YQIPJct_4Dc/s400/authentication-dialog.gif" alt="" id="BLOGGER_PHOTO_ID_5139093860473131186" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The dialog above presents an input and set of images.  The input accepts the user's challenge code and the images are part of a group configured by the user.  Only one of the images is the correct one, so if the user enters the correct challenge code and clicks on the correct image, access is granted.  Look ma, &lt;span style="font-style: italic;"&gt;no passwords!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Challenge Code&lt;/span&gt;...&lt;br /&gt;&lt;br /&gt;So where does this challenge code come from?  Well, during the registration process the user selects the method of delivery for the challenge code.  The options are sms, email, pager, or a discrete application.  The challenge code itself is a random set of characters with a minimum of ten characters (all numeric for pagers).  For email a complex uuid is used so copy/paste is usually required.&lt;br /&gt;&lt;br /&gt;You can access a non-working demo &lt;a href="http://www.raincitysoftware.com/openid/"&gt;here&lt;/a&gt;.  We hope to have a working demo soon...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-9144197963230930037?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/9144197963230930037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=9144197963230930037' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/9144197963230930037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/9144197963230930037'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/12/openid-with-flex-and-rails.html' title='OpenID with Flex and Rails'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_7lr-p1KyMn0/R1G7elTkUMI/AAAAAAAAAA4/r9Hm4IUK6Pw/s72-c/access-dialog.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-7615324858541604683</id><published>2007-11-30T12:43:00.000-08:00</published><updated>2007-12-01T12:11:27.061-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='database schema rails migration'/><title type='text'>Time Tracker Database Schema</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://raincitysoftware.com/docs/TimeTracker.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 300px;" src="http://raincitysoftware.com/docs/TimeTracker.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I'm working on a new project that was intended as a large scale demo for the &lt;a href="http://raincitysoftware.blogspot.com/2007/11/non-intrusive-flexrails-remoting.html"&gt;flex2rails&lt;/a&gt; library but has grown into it's own beast.   This article discusses the thinking behind the database design for time tracker.&lt;br /&gt;&lt;br /&gt;The image to the right is the full schema for time tracker as of version 2007.11.29.  I'll start with the basic business requirements to explain the entities.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Business Requirements&lt;/span&gt;:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;register users that belong to a company&lt;/li&gt;&lt;li&gt;register companies (customers) that have contacts&lt;/li&gt;&lt;li&gt;associate mailing address, email, phone, sms with contacts&lt;/li&gt;&lt;li&gt;support projects that have discrete tasks&lt;/li&gt;&lt;li&gt;associate users with projects&lt;br /&gt;&lt;/li&gt;&lt;li&gt;support time sheet entries made by users&lt;/li&gt;&lt;li&gt;support ad-hoc user attributes such as skill level, job title, etc.&lt;/li&gt;&lt;/ul&gt;And here are my ground rules for creating entities in rails projects:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;all tables have a discrete, autogenerated ID column as primary key&lt;/li&gt;&lt;li&gt;all tables include lock_version, created_at, updated_at columns (exceptions are for :through associations)&lt;/li&gt;&lt;li&gt;all tables include a bit_flags column that contains multiple boolean flags held in a single integer.  the mappings are defined in the rails model using the &lt;a href="http://raincityonrails.com/has_flags/"&gt;has_flags plugin&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;table names are plural&lt;/li&gt;&lt;li&gt;most columns are not-null&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;There are two basic entity groups:  user, contact and company related  and project related.  Lets start with the users.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Users&lt;/span&gt;:  The users table is configured for open ID.  it contains and open id URI, and email and optional name.  There is also an access_key column that will be used to continue login sessions across multiple applications, similar to a session id, but more flexible.  Bit flags, defined in the model, include active, admin, banned, etc.  An associated table is user_attributes.   This is used to store ad-hoc values such as job title, rolls, etc.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;You will notice that there is no password or password_hash column.  We use open id so don't require storing passwords in the user table.  But, if we did store passwords, the last place to do it is right along side the access/user code, or even in the same database.  So, if we ever have a need to actually store passwords, it would be in a separate database similar to a unix shadow file.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Contacts&lt;/span&gt;:  Entries into to contacts table may or may not be associated with users, typically they are.  Examples include project members, coders, etc. or customer contacts, managers, etc.  But, some customers are not registered users so they are not associated with the user table.  Other contacts may be virtual, such as "bill to" or "ship to".&lt;br /&gt;&lt;br /&gt;Contact methods define ways to communicate with a contact such as phone numbers, email addresses, sms, chat handles, etc.   Contacts may also be associated with zero or more mailing addresses that include physical coordinates.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Companies&lt;/span&gt;: Company rows may exist on their own but more typically they are associated with (and owners of) projects, have contacts, addresses, etc.  Each project must be associated with a company, typically the client/customer.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Projects&lt;/span&gt;: Projects are associated with a companies and can have project tasks and project members.  Projects can also themselves be associated with other projects as a sub-project.  Project members are registered users.  Projects typically have a start date.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Project Tasks&lt;/span&gt;: Project tasks belong to projects and users and have a single task type.  Tasks have a start date and due date and optionally number of hours bid.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Time Sheets&lt;/span&gt;: Time sheet entries are created by users to log time spent on project tasks.  Logged time includes a start time and number of hours.  There is also a single activity type associated with each time sheet.   Activity types are user defined and typically include design, coding, debugging, testing, etc.&lt;br /&gt;&lt;br /&gt;So this is a quick tour of the Time Tracker database schema.  The complete migration scripts are contained in the open source project and will be made available for download when the project goes to beta (projected for Q1-2008).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-7615324858541604683?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/7615324858541604683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=7615324858541604683' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7615324858541604683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/7615324858541604683'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/11/time-tracker-database-schema.html' title='Time Tracker Database Schema'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-6210294503635760156</id><published>2007-11-29T14:44:00.000-08:00</published><updated>2007-11-29T16:39:50.772-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails flex remoting rest flex2rails'/><title type='text'>Non Intrusive Flex/Rails Remoting</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Objective&lt;/span&gt;: create an open source remoting framework to communicate between Flex UI and Rails server without modifying any rails code.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Solution&lt;/span&gt;: Rails restful scaffold resource generation.&lt;br /&gt;&lt;br /&gt;Flex has some great xml processing tools.  Rails can easily generate xml out of the box.  So, match up the 7 rails restful methods with a Flex/AS3 framework and life is easy...&lt;br /&gt;&lt;br /&gt;This library includes connection configuration, index, show, create, update, destroy classes that operate against a remote Rails site. The rails code can be generated using resource scaffolding and remain unmodified.&lt;br /&gt;&lt;br /&gt;Here is a &lt;a href="http://raincitysoftware.com/ServicesClient/"&gt;live demo&lt;/a&gt;.  It's a simple list/detail application that uses the remoting framework.&lt;br /&gt;&lt;br /&gt;Download the swc &lt;a href="http://raincitysoftware.com/downloads/flex2rails-2007.11.28.swc"&gt;here&lt;/a&gt; or the &lt;a href="http://raincitysoftware.com/downloads/flex2rails-2007.11.28.tar.gz"&gt;source code&lt;/a&gt; here.  Or, you can check out the the latest by running:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;svn co http://svn.raincitysoftware.com/adobe/org-raincity-rails/src/&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Source code for the demo application is also available &lt;a href="http://raincitysoftware.com/downloads/flex2rails-demo.tar.gz"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Using the framework is quite easy.  Simply create a controller and register the rails host and services in your main application.  Then, create handlers for the discrete service methods including query (list), fetch (show), create, update and delete.  Custom methods can be created by extending RailsService and implementing the send() method.&lt;br /&gt;&lt;br /&gt;I'm currently updating the documentation with a step by step tutorial.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-6210294503635760156?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/6210294503635760156/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=6210294503635760156' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/6210294503635760156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/6210294503635760156'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/11/non-intrusive-flexrails-remoting.html' title='Non Intrusive Flex/Rails Remoting'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2139974165898401757.post-2016487005294646594</id><published>2007-11-29T11:33:00.000-08:00</published><updated>2007-11-29T14:42:56.087-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flex framework mvc'/><title type='text'>PureMVC - an Application Framework</title><content type='html'>I was introduced to &lt;a href="http://puremvc.org/"&gt;PureMVC&lt;/a&gt; a few weeks ago through &lt;a href="http://www.asserttrue.com/"&gt;assertTrue&lt;/a&gt;'s first rate video cast covering various Flex application frameworks including Cairngorm, MVCS, and others including my favorite, &lt;span style="font-style: italic;"&gt;none of the above&lt;/span&gt;.  But, PureMVC has many intriguing features, including...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;great documentation--the best I've seen short of having to buy a bulky book from amazon&lt;br /&gt;&lt;/li&gt;&lt;li&gt;great use of standard patterns&lt;br /&gt;&lt;/li&gt;&lt;li&gt;good use of publish/subscribe event model for loose coupling&lt;/li&gt;&lt;li&gt;promoted as lightweight, but feels like a java framework&lt;/li&gt;&lt;/ul&gt;So as a java engineer it does feel lightweight, especially after working with spring,  hibernate and j2EE--but after working with rails for the past two years, I still feel like it's a bit heavy.   But for someone looking for the best framework for Flex applications, PureMVC gets my vote.&lt;br /&gt;&lt;br /&gt;So why do I prefer &lt;span style="font-style: italic;"&gt;none of the above?  &lt;/span&gt;Well I'm not completely convinced, but I have a feeling that when a project gets to a usable size, the amount of registration for each command and value object would be overwelming.  I also am more familiar with logic inside the controller as opposed to inside discrete mediators.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2139974165898401757-2016487005294646594?l=raincitysoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://raincitysoftware.blogspot.com/feeds/2016487005294646594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2139974165898401757&amp;postID=2016487005294646594' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2016487005294646594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2139974165898401757/posts/default/2016487005294646594'/><link rel='alternate' type='text/html' href='http://raincitysoftware.blogspot.com/2007/11/puremvc-application-framework.html' title='PureMVC - an Application Framework'/><author><name>darryl west</name><uri>http://www.blogger.com/profile/13350374169529725641</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://3.bp.blogspot.com/_7lr-p1KyMn0/SmqUfZEpmcI/AAAAAAAAAGo/pkL5JaxMn8c/s1600-R/dpw-maggie-200x150.jpg'/></author><thr:total>0</thr:total></entry></feed>
