<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>Edge - Home</title>
  <id>tag:edge.matrixprojects.com,2009:mephisto/</id>
  <generator version="0.8.0" uri="http://mephistoblog.com">Mephisto Drax</generator>
  <link href="http://edge.matrixprojects.com/feed/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="http://edge.matrixprojects.com/" rel="alternate" type="text/html"/>
  <updated>2008-06-17T17:50:12Z</updated>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>stonean</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-06-17:21</id>
    <published>2008-06-17T17:14:00Z</published>
    <updated>2008-06-17T17:50:12Z</updated>
    <category term="Ruby"/>
    <category term="rails"/>
    <link href="http://edge.matrixprojects.com/2008/6/17/migration-naming-collisions" rel="alternate" type="text/html"/>
    <title>Migration Naming Collisions</title>
<content type="html">
            &lt;p&gt;In a very short amount of time, I realized that using a timestamp for the migration number was too cumbersome and I think it is a mistake that will hopefully be corrected in future versions of Rails.&lt;/p&gt;

&lt;p&gt;That started me thinking about the conversion that would be necessary to move away from timestamped migrations and I didn't like what I was foreseeing.  Instead of going through that headache, I decided to stick with the standard version number scheme, this &lt;a href=&quot;http://blog.stonean.com/2008/06/timestamped-migrations.html&quot;&gt;post&lt;/a&gt; shows you how to do this.&lt;/p&gt;

&lt;p&gt;So how do you solve the migration collision issue without this timestamp feature?  This is actually easy, just create a file named &quot;version&quot; in your db/migration directory.  If you are working on a team and are going to create a new migration (or more), just edit this file and put the highest number migration in your source and commit it.&lt;/p&gt;

&lt;p&gt;This will prevent you from having to commit your migration and your team can be notified of the next version number they should use. &lt;/p&gt;

&lt;p&gt;After all, as developers, we need to communicate when working on a project. If you don't, you are asking for trouble.  &lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>stonean</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-06-06:20</id>
    <published>2008-06-06T16:16:00Z</published>
    <updated>2008-06-06T16:28:20Z</updated>
    <category term="Ruby"/>
    <category term="rails"/>
    <link href="http://edge.matrixprojects.com/2008/6/6/nested-resources" rel="alternate" type="text/html"/>
    <title>Nested Resources</title>
<content type="html">
            &lt;p&gt;
Nested resources sound nice and look cool on the url: 
&lt;/p&gt;
&lt;pre&gt;http://wickedcoolblog.com/posts/1/comments&lt;/pre&gt;
&lt;p&gt;
In order for this to work, you have to define this relationship in your routes file as such:
&lt;/p&gt;
&lt;pre&gt;
  map.resources :posts do |posts|
    posts.resources :comments
  end
&lt;/pre&gt;
&lt;p&gt;
So what does this mean from a content structure?  Does the comments controller reside in a posts directory?  Is the comments controller namespaced with posts?
&lt;/p&gt;
&lt;p&gt;
Fortunately neither of these constructs are required.  Quite simply, when you call the above url you are getting the following parameters:
&lt;/p&gt;
&lt;pre&gt;{&quot;action&quot;=&gt;&quot;index&quot;, &quot;post_id&quot;=&gt;&quot;1&quot;, &quot;controller&quot;=&gt;&quot;comments&quot;} &lt;/pre&gt;
&lt;p&gt;
As you can see, you're still just accessing the comments controller and the only thing special about it is the post_id parameter.  Your comments controller needs to process this parameter to ensure that all comments belong to the post_id given.
&lt;/p&gt;
&lt;p&gt;
In the resource oriented construct of a REST (Representational Stage Transfer) design this doesn't quite match.  With the above nested route definition, you won't be able to access the comments resource without going through the posts resource.  This can be fixed with a slight addition to your routes file:
&lt;/p&gt;
&lt;pre&gt;
  map.resources :posts do |posts|
    posts.resources :comments
  end

  map.resources :comments
&lt;/pre&gt;
&lt;p&gt;
Notice that comments are defined twice.  This now gives you the ability to access:
&lt;/p&gt;
&lt;pre&gt;http://wickedcoolblog.com/posts/1/comments&lt;/pre&gt;&lt;p&gt;and&lt;p&gt;
&lt;pre&gt;http://wickedcoolblog.com/comments&lt;/pre&gt;
&lt;p&gt;
Now we have an issue with our comments controller.  If we try to access &quot;/comments&quot;, we don't get the post_id passed but our previous code (with the nested resource) depended on it.
&lt;/p&gt;
&lt;p&gt;
If you had designed your initial controller with a before filter, this change would be a lot easier.  For example:
&lt;pre&gt;
class CommentsController &amp;lt; ApplicationController
	before_filter :find_models, :only =&gt; :index
	before_filter :find_model_instance, 
                           :only =&gt; [:show, :edit, :update, :destroy]
...
  private
    def find_models
      @comments = Comment.find(:all, 
                        :conditions =&gt; [&quot;comments.post_id = ?&quot;, params[:post_id]])
    end

    def find_model_instance
      @comment = Comment.find(params[:id], 
                       :conditions =&gt; [&quot;comments.post_id = ?&quot;, params[:post_id]])
    end
end
&lt;/pre&gt;
&lt;p&gt;
To add the ability to access the comments resource without going through products, you need to modify this a bit:
&lt;/p&gt;
&lt;pre&gt;
class CommentsController &amp;lt; ApplicationController
	before_filter :find_models, :only =&gt; :index
	before_filter :find_model_instance, 
                           :only =&gt; [:show, :edit, :update, :destroy]
...
  private
    def find_models
      if params[:post_id]
        @comments = Comment.find(:all, 
                        :conditions =&gt; [&quot;comments.post_id = ?&quot;, params[:post_id]])
      else
        @comments = Comment.find(:all)
      end
    end

    def find_model_instance
      if params[:post_id]
        @comment = Comment.find(params[:id], 
                       :conditions =&gt; [&quot;comments.post_id = ?&quot;, params[:post_id]])
      else
        @comment = Comment.find(params[:id])
      end   
    end
end
&lt;/pre&gt;
&lt;p&gt;
**Note: This could be refactored to be more &quot;DRY&quot; and should have some security code added, but will serve for demonstration purposes.
&lt;/p&gt;
&lt;p&gt;
There has been quite a lot of discussion on nested resources and issues with people not understanding when to use them. So, coming up soon is an article focused on what to look for when making a decision to use nested resources.  
&lt;/p&gt;
&lt;p&gt;
Nothing should be used all the time.  In the case of nested resources, there only a few times they should be used and never more than one level deep.
&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>stonean</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-27:19</id>
    <published>2008-05-27T14:20:00Z</published>
    <updated>2008-05-27T14:20:55Z</updated>
    <category term="Ruby"/>
    <category term="ruby"/>
    <link href="http://edge.matrixprojects.com/2008/5/27/depends_on" rel="alternate" type="text/html"/>
    <title>depends_on</title>
<content type="html">
            &lt;p&gt;For one of my projects I had the need for a polymorphic association in which one object required the existence of the other.  I had written a specific version of this, but decided it could easily be abstracted.  I didn't go with the built in ActiveRecord polymorphic features as I wanted to strip it down to the basics to see if it would work.  Why?  I would love to use this in DataMapper at some point.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
module Stonean
  module DependsOn
    def self.included(base)
      base.extend Stonean::DependsOn::ClassMethods
    end

    module ClassMethods
      def depends_on(model_sym, options = {}) 
        has_one model_sym, 
                :foreign_key =&amp;gt; &amp;quot;#{options[:as]}_id&amp;quot;,
                :conditions =&amp;gt; &amp;quot;#{options[:as]}_type = '#{self.name}'&amp;quot;

        validates_presence_of model_sym
        validates_associated model_sym

        define_save_method(model_sym, options[:as])
        before_save &amp;quot;save_#{model_sym}&amp;quot;.to_sym

        options[:attrs].each{&#124;attr&#124; define_accessors(model_sym, attr)}
      end
      
      def define_save_method(model_sym, poly)
        define_method &amp;quot;save_#{model_sym}&amp;quot; do
          eval(&amp;quot;self.#{model_sym}.#{poly}_type = self.class.name&amp;quot;)
          eval(&amp;quot;self.#{model_sym}.#{poly}_id = self.id&amp;quot;)
          eval(&amp;quot;self.#{model_sym}.save&amp;quot;)
        end
      end

      def define_accessors(model_sym, attr)
        define_method attr do
          eval(&amp;quot;self.#{model_sym} ? self.#{model_sym}.#{attr} : nil&amp;quot;)
        end

        define_method &amp;quot;#{attr}=&amp;quot; do &#124;val&#124;
          model_defined = eval(&amp;quot;self.#{model_sym}&amp;quot;)

          unless model_defined
           eval(&amp;quot;self.#{model_sym} = self.build_#{model_sym}&amp;quot;)
          end

          eval(&amp;quot;self.#{model_sym}.#{attr}= val&amp;quot;)
        end
      end

    end
  end
end
ActiveRecord::Base.send :include, Stonean::DependsOn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Here's an example, but first a little information.  Content is a model that all &quot;presentable&quot; objects in my cms depend on.  It holds the name and url attributes so when you are calling message.name, you are really calling message.content.name.  
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Message &amp;lt; ActiveRecord::Base
  depends_on :content, :attrs =&amp;gt; [:name, :url], :as =&amp;gt; :presentable
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
This means your form and views can use these methods and not worry about the underlying association.  for example:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; text_field_tag &amp;quot;message[name]&amp;quot;, value
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Now you don't have to do anything special in your views or controller to build and save the depends_on object.  
&lt;/p&gt;
&lt;p&gt;
This example is very specific, but with a little modification, can be used to implement a class table inheritance architecture.  I definitely plan on doing this soon.
&lt;/p&gt;
&lt;p&gt;
So what are the drawbacks?  The major one is the find method.  As it's written now, you will make two calls to the database:  one for the main object and one for the dependent object.  That blows.  I will be addressing this issue very soon.
&lt;/p&gt;
&lt;p&gt;
I will be releasing this as a gem in the near future, but for now you can just copy the code above and add it into your config/initializers directory.
&lt;/p&gt;
&lt;p&gt;
If you have any ideas or suggestions, I would love to hear them.
&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>stonean</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-21:18</id>
    <published>2008-05-21T16:53:00Z</published>
    <updated>2008-05-21T17:00:27Z</updated>
    <category term="Ruby"/>
    <category term="ruby"/>
    <category term="teamcity"/>
    <link href="http://edge.matrixprojects.com/2008/5/21/continuous-integration-and-teamcity" rel="alternate" type="text/html"/>
    <title>Continuous Integration and TeamCity</title>
<content type="html">
            &lt;p&gt;In our role as a &quot;Solutions Provider&quot;, it is important to provide reassurances that the code we are delivering is solid.  One way of doing so is to utilize testing frameworks such as &lt;a href=&quot;http://rspec.info/&quot;&gt;RSpec&lt;/a&gt;.  Writing tests provide more functionality than proving code &quot;should do X&quot;, writing tests helps developers understand the goal of the code they are writing.  A positive side effect of this construct is that properly written tests will also let you know when other developers (or even the author) attempt to use the code incorrectly.&lt;/p&gt;

&lt;p&gt;Testing frameworks aren't the panacea for all project issues, but when used properly, they are very effective.  When working in a team environment, one developer may alter a piece of code or configuration setting that is unknowingly used elsewhere.  While their local tests should catch the issue, not everyone remembers to run the tests before they commit. In order to prevent these sort of issues we decided to implement the practice of &lt;a href=&quot;http://martinfowler.com/articles/continuousIntegration.html&quot;&gt;Continous Integration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To facilitate the process we decided to evaluate &lt;a href=&quot;http://www.jetbrains.com/teamcity/&quot;&gt;TeamCity&lt;/a&gt;.  TeamCity is a continuous integration system that utilizes a distributed agent model and supports multiple Java and .Net build runners as well as a Rake.  Having this multi-platform/technology support means TeamCity will be able to handle all the technologies we work with and the distributed agent model allows us to run multiple project tests at once.&lt;/p&gt;

&lt;p&gt;We have configured TeamCity for use with Ruby and .Net (Java setup coming soon) and are still in evaluation mode.  We haven't really put it through the paces yet, but are pleased with the product and will continue forward.&lt;/p&gt;

&lt;p&gt;More info on TeamCity coming soon...&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>phil</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-21:17</id>
    <published>2008-05-21T14:14:00Z</published>
    <updated>2008-05-21T14:14:55Z</updated>
    <category term="Java"/>
    <category term="grails"/>
    <category term="groovy"/>
    <link href="http://edge.matrixprojects.com/2008/5/21/ajax-like-file-upload-and-download-in-grails" rel="alternate" type="text/html"/>
    <title>AJAX-like File Upload and Download in Grails</title>
<content type="html">
            &lt;p&gt;I recently had the opportunity to create a simple application in &lt;a href=&quot;http://www.grails.org&quot;&gt;Grails&lt;/a&gt;.  The application was nothing more than an &lt;a href=&quot;http://en.wikipedia.org/wiki/Extract%2C_transform%2C_load&quot;&gt;&lt;span class=&quot;caps&quot;&gt;ETL&lt;/span&gt;&lt;/a&gt; process, with a single screen for uploading and downloading files.  I wanted to be able to provide the user feedback without going through a redirect-after-post scenario.  It turns out that this is relatively straightforward, once you know all the details.&lt;/p&gt;


	&lt;p&gt;First, I say &#8220;AJAX-like&#8221; file upload because it&#8217;s not actually possible to send multipart encoded form data through an &lt;a href=&quot;http://en.wikipedia.org/wiki/Xmlhttprequest&quot;&gt;XMLHttpRequest&lt;/a&gt; object (AJAX&#8217;s core communications object).  However, we can achieve similar functionality by using an &lt;span class=&quot;caps&quot;&gt;IFRAME&lt;/span&gt;.  Here&#8217;s an example of what the form might look like in Grails:&lt;/p&gt;


&lt;pre class=&quot;eiffel&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;id&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;status&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;style&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;height:100px;width: 510px;margin: 10px auto;padding: 10px;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;message&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;id&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;message&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;style&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;display:${flash.message ? 'block':'none'}&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
${flash.message}&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;errors&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;id&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;errors&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;style&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;display:${flash.error ? 'block':'none'}&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
${flash.error}&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;id&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;upload_progress&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;style&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;display:none;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Loading...&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;        
&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;id&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;content&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;form&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;method&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;post&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;action&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;save&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;enctype&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;multipart/form-data&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;
&lt;span class=&quot;MarkupTag&quot;&gt;target=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;upload_target&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;onsubmit&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;$('upload_progress').show();return true;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;type&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;file&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;name&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;file&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;size&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;45&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;type&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;submit&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;iframe&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;id&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;upload_target&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;name&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;upload_target&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;#&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;
&lt;span class=&quot;MarkupTag&quot;&gt;style=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;width:0;height:0;border:0px solid #fff;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;iframe&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;...and the associated Grails controller method for receiving the file&#8230;&lt;/p&gt;


&lt;pre class=&quot;eiffel&quot;&gt;
def save = {
    def file = request.getFile(&lt;span class=&quot;String&quot;&gt;'file'&lt;/span&gt;)
    &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; (file.originalFilename) {
        &lt;span class=&quot;Keyword&quot;&gt;try&lt;/span&gt; {
            fileProcessingService.processImportFile(file)
            flash.error = &lt;span class=&quot;BuiltInConstant&quot;&gt;null&lt;/span&gt;
            flash.message = &lt;span class=&quot;String&quot;&gt;&amp;quot;Import of '${file.originalFilename}' complete.&amp;quot;&lt;/span&gt;
        }
        &lt;span class=&quot;Keyword&quot;&gt;catch&lt;/span&gt; (&lt;span class=&quot;LibraryObject&quot;&gt;Exception&lt;/span&gt; e) {
            flash.error = &lt;span class=&quot;String&quot;&gt;&amp;quot;There was an error importing '${file.originalFilename}':  &lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;${e.message} (cause:${e.cause})&amp;quot;&lt;/span&gt;
            e.printStackTrace()
        }
    }
    &lt;span class=&quot;Keyword&quot;&gt;else&lt;/span&gt; {
        flash.error = &lt;span class=&quot;String&quot;&gt;&amp;quot;Please specify a filename to upload.&amp;quot;&lt;/span&gt;
    }
}
&lt;/pre&gt;

	&lt;p&gt;The &lt;code&gt;fileProcessingService&lt;/code&gt; is a Spring injected service that does the actual parsing and loading of data into the database.  However, one thing you might wonder is &#8220;if this is all happening in an &lt;span class=&quot;caps&quot;&gt;IFRAME&lt;/span&gt;, how do the flash messages/errors get displayed outside of the &lt;span class=&quot;caps&quot;&gt;IFRAME&lt;/span&gt;?&#8221;  This is done via JavaScript that&#8217;s eval&#8217;d in the (automatically returned) &lt;code&gt;save.gsp&lt;/code&gt; file.&lt;/p&gt;


&lt;pre class=&quot;eiffel&quot;&gt;
&lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;script language&lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;javascript&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; type&lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;text/javascript&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;LibraryObject&quot;&gt;window&lt;/span&gt;.&lt;span class=&quot;LibraryConstant&quot;&gt;top&lt;/span&gt;.&lt;span class=&quot;LibraryObject&quot;&gt;window&lt;/span&gt;.stopUpload(&lt;span class=&quot;JsOperator&quot;&gt;$&lt;/span&gt;{flash&lt;span class=&quot;LibraryFunction&quot;&gt;.error&lt;/span&gt; ? &lt;span class=&quot;BuiltInConstant&quot;&gt;false&lt;/span&gt; : &lt;span class=&quot;BuiltInConstant&quot;&gt;true&lt;/span&gt;},
&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;${flash.error ? flash.error.encodeAsHTML() : flash.message.encodeAsHTML()}&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
&lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;/script&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;...and the &lt;code&gt;stopUpload(...);&lt;/code&gt; function looks like this (in &lt;code&gt;index.gsp&lt;/code&gt;):&lt;/p&gt;


&lt;pre class=&quot;eiffel&quot;&gt;
&lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;script language&lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;javascript&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; type&lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;text/javascript&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; called on return of the form from the IFRAME&lt;/span&gt;
  function &lt;span class=&quot;FunctionName&quot;&gt;stopUpload&lt;/span&gt;(&lt;span class=&quot;FunctionParameter&quot;&gt;success, msg&lt;/span&gt;) {
    &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; we're done uploading, so hide the progress indicator&lt;/span&gt;
    &lt;span class=&quot;JsOperator&quot;&gt;$&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;upload_progress&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).hide();
    &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; success is returned in the partial&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; (success) {
      &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; set the HTML to the message returned&lt;/span&gt;
      &lt;span class=&quot;JsOperator&quot;&gt;$&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;message&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).innerHTML &lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt; msg;
      &lt;span class=&quot;JsOperator&quot;&gt;$&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;message&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).show();
    }
    &lt;span class=&quot;Keyword&quot;&gt;else&lt;/span&gt; {
      &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; set the HTML to the error returned&lt;/span&gt;
      &lt;span class=&quot;JsOperator&quot;&gt;$&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;errors&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).innerHTML &lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt; msg;
      &lt;span class=&quot;JsOperator&quot;&gt;$&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;errors&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).show();
    }
    &lt;span class=&quot;Keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;BuiltInConstant&quot;&gt;true&lt;/span&gt;;
  }
&lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;/script&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;Hopefully most of the code + commentary makes this self explanatory.  Comment if it&#8217;s not and I&#8217;ll edit the article.&lt;/p&gt;


	&lt;p&gt;Download &#8220;ajax-like&#8221; streaming works similarly with an &lt;span class=&quot;caps&quot;&gt;IFRAME&lt;/span&gt;:&lt;/p&gt;


&lt;pre class=&quot;eiffel&quot;&gt;
&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;#&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;onclick&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;download('$&lt;span class=&quot;StringInterpolation&quot;&gt;{createLink(action:&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;exportFile&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)}&lt;/span&gt;');&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Export Data&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;iframe&lt;/span&gt; &lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTagAttribute&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;download_target&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;name&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;download_target&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MarkupTagAttribute&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;#&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;
&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTagAttribute&quot;&gt;style&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;width:0;height:0;border:0px solid #fff;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;MarkupNameOfTag&quot;&gt;iframe&lt;/span&gt;&lt;span class=&quot;MarkupTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;...and the &lt;code&gt;download(...)&lt;/code&gt; function in JavaScript&#8230;&lt;/p&gt;


&lt;pre class=&quot;eiffel&quot;&gt;
&lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;script language&lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;javascript&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; type&lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;text/javascript&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
  function &lt;span class=&quot;FunctionName&quot;&gt;download&lt;/span&gt;(&lt;span class=&quot;FunctionParameter&quot;&gt;url&lt;/span&gt;) {
    &lt;span class=&quot;JsOperator&quot;&gt;$&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;download_target&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).&lt;span class=&quot;LibraryConstant&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt; url;
    &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; do AJAX stuff here, such as below&lt;/span&gt;
    &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; new Ajax.Updater('a_div_id','/a_url',{asynchronous:true,evalScripts:true});&lt;/span&gt;
  }
&lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;/script&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;...and the &lt;code&gt;exportFile&lt;/code&gt; Grails controller method&#8230;&lt;/p&gt;


&lt;pre class=&quot;eiffel&quot;&gt;
def exportFile = {
    &lt;span class=&quot;Keyword&quot;&gt;try&lt;/span&gt; {
        def results = fileProcessingService.createFileExport()
        &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; (results) {
            response.setContentType(&lt;span class=&quot;String&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;)
            response.setHeader(&lt;span class=&quot;String&quot;&gt;&amp;quot;Content-disposition&amp;quot;&lt;/span&gt;, 
&lt;span class=&quot;String&quot;&gt;&amp;quot;attachment; filename=download-file.txt&amp;quot;&lt;/span&gt;)
            response.outputStream &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&lt;/span&gt; results
        }
        &lt;span class=&quot;Keyword&quot;&gt;else&lt;/span&gt; {
            flash.message = 
&lt;span class=&quot;String&quot;&gt;&amp;quot;There was no data to export.  Please import a new file before exporting.&amp;quot;&lt;/span&gt;
            render(template:&lt;span class=&quot;String&quot;&gt;'updateMessage'&lt;/span&gt;)
        }
    }
    &lt;span class=&quot;Keyword&quot;&gt;catch&lt;/span&gt; (&lt;span class=&quot;LibraryObject&quot;&gt;Exception&lt;/span&gt; e) {
        flash.error = 
&lt;span class=&quot;String&quot;&gt;&amp;quot;There was an error exporting the download file:  ${e.message} (cause: ${e.cause})&amp;quot;&lt;/span&gt;
        e.printStackTrace()
        render(template:&lt;span class=&quot;String&quot;&gt;'updateMessage'&lt;/span&gt;)
    }
}
&lt;/pre&gt;

	&lt;p&gt;...and finally the &lt;code&gt;_updateMessage.gsp&lt;/code&gt; template (note the &#8217;_&#8217; indicating it&#8217;s a partial template):&lt;/p&gt;


&lt;pre class=&quot;eiffel&quot;&gt;
&lt;span class=&quot;JsOperator&quot;&gt;$&lt;/span&gt;{javascript(library:&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;prototype&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)}
&lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;script type&lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;text/javascript&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;g:&lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; test&lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;${flash.message}&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
        parent.&lt;span class=&quot;LibraryObject&quot;&gt;document&lt;/span&gt;.&lt;span class=&quot;LibraryFunction&quot;&gt;getElementById&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;message&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).innerHTML &lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;${flash.message}&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
        parent.&lt;span class=&quot;LibraryObject&quot;&gt;document&lt;/span&gt;.&lt;span class=&quot;LibraryFunction&quot;&gt;getElementById&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;message&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).show();
    &lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;/g:&lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;g:elseif test&lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;${flash.error}&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
        parent.&lt;span class=&quot;LibraryObject&quot;&gt;document&lt;/span&gt;.&lt;span class=&quot;LibraryFunction&quot;&gt;getElementById&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;errors&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).innerHTML &lt;span class=&quot;JsOperator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;${flash.error}&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
        parent.&lt;span class=&quot;LibraryObject&quot;&gt;document&lt;/span&gt;.&lt;span class=&quot;LibraryFunction&quot;&gt;getElementById&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;errors&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).show();
    &lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;/g:elseif&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;JsOperator&quot;&gt;&amp;lt;&lt;/span&gt;/script&lt;span class=&quot;JsOperator&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;Note that this partial&#8217;s JavaScript uses a slightly different technique to directly access the parent page&#8217;s &lt;span class=&quot;caps&quot;&gt;DOM&lt;/span&gt; to update the elements (rather than calling a JavaScript function in the parent page).&lt;/p&gt;


	&lt;p&gt;So there&#8217;s almost the entire coded needed to upload a file or download a file &#8220;AJAX-like&#8221; from and to a browser &#8211; shoot me questions or comments.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>phil</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-21:16</id>
    <published>2008-05-21T13:56:00Z</published>
    <updated>2008-05-21T14:15:03Z</updated>
    <category term="Ruby"/>
    <category term="mysql"/>
    <category term="rails"/>
    <category term="rake"/>
    <category term="ruby"/>
    <link href="http://edge.matrixprojects.com/2008/5/21/backing-up-your-rails-mysql-db-via-a-rake-task" rel="alternate" type="text/html"/>
    <title>Backing up your Rails MySQL DB via a Rake task</title>
<content type="html">
            &lt;p&gt;&lt;a href=&quot;http://blog.craigambrose.com/articles/2007/03/01/a-rake-task-for-database-backups&quot;&gt;This article&lt;/a&gt; is the source of this code, but I&#8217;ve included some of the recommended modifications in the comments.  This will back up a MySQL database (via &lt;code&gt;mysqldump&lt;/code&gt;) into a directory of your choosing (or &lt;span class=&quot;caps&quot;&gt;RAILS&lt;/span&gt;_ROOT/db by default), rolling automatically at 20 backups, and using your database.yml as the source of the login information (so you keep it &lt;span class=&quot;caps&quot;&gt;DRY&lt;/span&gt;).&lt;/p&gt;


&lt;pre class=&quot;eiffel&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;find&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; 
  namespace &lt;span class=&quot;UserDefinedConstant&quot;&gt;&lt;span class=&quot;UserDefinedConstant&quot;&gt;:&lt;/span&gt;db&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do  &lt;/span&gt;desc &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;Backup the database to a file. Options: DIR=base_dir &lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;RAILS_ENV=production MAX=20&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    task &lt;span class=&quot;UserDefinedConstant&quot;&gt;&lt;span class=&quot;UserDefinedConstant&quot;&gt;:&lt;/span&gt;backup&lt;/span&gt; =&amp;gt; [&lt;span class=&quot;UserDefinedConstant&quot;&gt;&lt;span class=&quot;UserDefinedConstant&quot;&gt;:&lt;/span&gt;environment&lt;/span&gt;] &lt;span class=&quot;Keyword&quot;&gt;do&lt;/span&gt;
      datestamp &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;LibraryObject&quot;&gt;Time&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;now&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;strftime&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;%Y-%m-%d_%H-%M-%S&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)    
      base_path &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;ENV&lt;/span&gt;[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;DIR&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;] &lt;span class=&quot;Keyword&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;db&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
      backup_base &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;LibraryObject&quot;&gt;File&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;join&lt;/span&gt;(base_path, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;backup&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)
      backup_folder &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;LibraryObject&quot;&gt;File&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;join&lt;/span&gt;(backup_base, datestamp)
      backup_file &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;LibraryObject&quot;&gt;File&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;join&lt;/span&gt;(backup_folder, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;Variable&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;_dump.sql.gz&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)    
      &lt;span class=&quot;LibraryObject&quot;&gt;File&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;makedirs&lt;/span&gt;(backup_folder)    
      db_config &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;LibraryObject&quot;&gt;ActiveRecord&lt;/span&gt;::&lt;span class=&quot;FunctionName&quot;&gt;Base&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;configurations&lt;/span&gt;[&lt;span class=&quot;Variable&quot;&gt;RAILS_ENV&lt;/span&gt;]   
      sh &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;mysqldump -u &lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;#{&lt;/span&gt;db_config&lt;span class=&quot;StringInterpolation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;username&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;FunctionName&quot;&gt;to_s&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;-p&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; db_config&lt;span class=&quot;StringInterpolation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;password&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;#{&lt;/span&gt;db_config&lt;span class=&quot;StringInterpolation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;password&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;FunctionName&quot;&gt;to_s&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; --opt &lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;#{&lt;/span&gt;db_config&lt;span class=&quot;StringInterpolation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;database&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; | &lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;gzip -c &amp;gt; &lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;#{&lt;/span&gt;backup_file&lt;span class=&quot;StringInterpolation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;     
      dir &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;LibraryObject&quot;&gt;Dir&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;new&lt;/span&gt;(backup_base)
      all_backups &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; (dir.&lt;span class=&quot;FunctionName&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; [&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;.&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;..&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;]).&lt;span class=&quot;FunctionName&quot;&gt;sort&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;reverse&lt;/span&gt;
      puts &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;Created backup: &lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;#{&lt;/span&gt;backup_file&lt;span class=&quot;StringInterpolation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;     
      max_backups &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;ENV&lt;/span&gt;[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;MAX&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;] &lt;span class=&quot;Keyword&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;Number&quot;&gt;20&lt;/span&gt;
      unwanted_backups &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; all_backups[max_backups.&lt;span class=&quot;FunctionName&quot;&gt;to_i&lt;/span&gt;..&lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;] &lt;span class=&quot;Keyword&quot;&gt;||&lt;/span&gt; []
      &lt;span class=&quot;Keyword&quot;&gt;for&lt;/span&gt; unwanted_backup &lt;span class=&quot;Keyword&quot;&gt;in&lt;/span&gt; unwanted_backups
      &lt;span class=&quot;LibraryObject&quot;&gt;FileUtils&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;rm_rf&lt;/span&gt;(&lt;span class=&quot;LibraryObject&quot;&gt;File&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;join&lt;/span&gt;(backup_base, unwanted_backup))
      puts &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;deleted &lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;#{&lt;/span&gt;unwanted_backup&lt;span class=&quot;StringInterpolation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
    puts &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;Deleted &lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;#{&lt;/span&gt;unwanted_backups&lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;FunctionName&quot;&gt;length&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; backups, &lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;#{&lt;/span&gt;all_backups&lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;FunctionName&quot;&gt;length&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;unwanted_backups&lt;span class=&quot;StringInterpolation&quot;&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;FunctionName&quot;&gt;length&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;StringInterpolation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; backups available&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;I created a cron entry like so to backup my mephisto database every day at 3am (EST):&lt;/p&gt;


&lt;pre class=&quot;eiffel&quot;&gt;
0 3 * * * cd /srv/mephisto-0.8/ &lt;span class=&quot;Keyword&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; /usr/local/bin/rake db:backup RAILS_ENV=production
&lt;/pre&gt;

	&lt;p&gt;Of course if your running on a &lt;span class=&quot;caps&quot;&gt;VPS&lt;/span&gt; or hosted solution, your provider is likely doing their own backups, but if you&#8217;re not, this can come in handy.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>chad</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-09:14</id>
    <published>2008-05-09T18:41:00Z</published>
    <updated>2008-05-09T18:42:44Z</updated>
    <category term="Agile"/>
    <category term="agile"/>
    <category term="contracts"/>
    <link href="http://edge.matrixprojects.com/2008/5/9/agile-contracts" rel="alternate" type="text/html"/>
    <title>Agile Contracts</title>
<content type="html">
            &lt;p&gt;Over the last several years I have been actively engaged in contract creation and negotiation between my company and our clients for professional services and project work.  My responsibilities have included reviewing and creating contractual documentation defining the scope, deliverables, timeline, and approach.  The artifact used to for this contract negotiation is a standard Statement of Work (SOW) commonly used in various industries.  The premise of a &lt;span class=&quot;caps&quot;&gt;SOW&lt;/span&gt; is to detail the exact work that will be done for a given engagement.&lt;/p&gt;


	&lt;p&gt;Additionally, I have been one of the principle executors of Agile methodologies for software delivery within our company.  We have been successfully engaging with various clients for the last fours years managing our development process with a &lt;span class=&quot;caps&quot;&gt;SCRUM&lt;/span&gt; like development methodology.  The adroit reader will immediately recognize the incompatibility of this Agile process with our contractual process mentioned above.  I have been struggling over the last several years to adapt our &lt;span class=&quot;caps&quot;&gt;SOW&lt;/span&gt; to support Agile processes.  In short I feel that my efforts have fallen short and possibly failed.&lt;/p&gt;


	&lt;p&gt;In an effort to amend our contractual approaches I have had the opportunity to study various contract models.  I have also discovered several resources that have proved valuable to understanding potential contractual approaches.  I hope you find these resources useful as well.&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://www.poppendieck.com/pdfs/Contracts_Excerpt_from_Lean_Software_Devleopment.pdf&quot;&gt;Lean Software Development Excerpt&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://cyrusinnovation.com/downloads/agile2005_cyrusinnovation_targetcostcontracts_paper.pdf&quot;&gt;Selling Target-Cost Contracts&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://dataforeningen.no/?module=Articles;action=ArticleFolder.publicOpenFolder;ID=1044&quot;&gt;&lt;span class=&quot;caps&quot;&gt;PS 2000&lt;/span&gt; Standard Contract&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://alistair.cockburn.us/index.php/Agile_contracts&quot;&gt;Alistair Cockburn &#8211; Agile Contracts&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://www.cutter.com/content-and-analysis/resource-centers/agile-project-management/sample-our-research/apmu0617/apmu0617.pdf&quot;&gt;Contracting Agile Projects&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;And of course some interesting tidbits (conceptually) from Martin Fowler&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://martinfowler.com/bliki/ScopeLimbering.html&quot;&gt;Scope Limbering&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://martinfowler.com/bliki/FixedScopeMirage.html&quot;&gt;Fixed Scope Mirage&lt;/a&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>russell</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-09:13</id>
    <published>2008-05-09T14:49:00Z</published>
    <updated>2008-05-09T15:13:36Z</updated>
    <category term=".Net"/>
    <category term=".net"/>
    <category term="asp.net"/>
    <category term="linq"/>
    <link href="http://edge.matrixprojects.com/2008/5/9/linq-to-sql-what-s-next" rel="alternate" type="text/html"/>
    <title>LINQ to SQL: What's Next?</title>
<content type="html">
            &lt;p&gt;LINQ-to-SQL has been a huge success with the .NET community. While other ORM frameworks such as nHibernate, SubSonic, etc...have been available for years, many Microsoft shops prefer to work with Microsoft offerings when available. For those nHibernate users out there, the visual designer for LINQ-to-SQL is also a welcome feature in Visual Studio--no more XML files to edit. 
&lt;/p&gt;
&lt;p&gt;
As most already know, LINQ-to-SQL is only for use with SQL Server and for projects with a near 1:1 relationship between the database schema and domain model. What if you want to use Oracle? What if your domain model differs from the underlying relational structure? LINQ-to-Entities addresses these concerns. 
&lt;/p&gt;
&lt;p&gt;
LINQ-to-Entities is the next major addition to ADO.NET, and it provides a much fuller feature set than LINQ-to-SQL such as:
&lt;ul&gt;
&lt;li&gt;Domain Modeling Capabilities and the ability to define conceptual, data store agnostic models&lt;/li&gt;
&lt;li&gt;Object Relational Mapping Capabilities (full CRUD and state management scenarios)&lt;/li&gt;
&lt;li&gt;Database independent Query Capabilities using LINQ or Entity SQL&lt;/li&gt;
&lt;/ul&gt;
The ability to work with a conceptual model versus a relational model is really the highlight of LINQ-to-Entities. For an in depth comparison of LINQ-to-SQL and LINQ-to-Entities, check out this pre-release documentation on &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/cc161164.aspx&quot;&gt;MSDN&lt;/a&gt;.
&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>russell</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-09:12</id>
    <published>2008-05-09T13:49:00Z</published>
    <updated>2008-05-09T14:45:32Z</updated>
    <category term=".Net"/>
    <category term="asp.net"/>
    <category term="mvc"/>
    <link href="http://edge.matrixprojects.com/2008/5/9/asp-net-mvc" rel="alternate" type="text/html"/>
    <title>ASP.NET MVC</title>
<content type="html">
            &lt;p&gt;If you are like me, you've been developing in ASP.NET since the beginning, and you truly enjoy the development experience. Like many other .NET developers, I had some experience with VB and Classic ASP before I moved to .NET. At the time, the Page Controller model was a familiar friend from the Windows development world. Someone well versed in WinForms development could transition to web work with minimal fuss. In most cases, this still holds true today in the .NET framework. 
&lt;/p&gt;
&lt;p&gt;
The PostBack model in ASP.NET does a very good job of shielding the developer from the inner workings of HTTP. This speeds development time for the most part, and it lessens the learning curve for new developers. However, some developers felt that the PostBack model held back too much from the developer. This made other frameworks such as Ruby on Rails more appealing, and thus spawned projects such as Castle Monorail.
&lt;/p&gt;
&lt;p&gt;
The popularity of Rails and the Castle Project (as well as other MVC frameworks) finally caught the attention of Microsoft. Which brings us to ASP.NET MVC. ASP.NET MVC is welcome addition to the .NET world--it provides a true MVC architecture for web applications. Ruby on Rails developers and Monorail developers will both see some similarity in Microsoft's implementation. In fact, Microsoft has left hooks in ASP.NET MVC for Castle Monorail components. 
&lt;/p&gt;
&lt;p&gt;
In the end, ASP.NET MVC won't just be a Rails clone, but rather a best of both worlds framework. True MVC architecture with the power and flexibility of ASP.NET server side controls. You can get the latest version ASP.NET MVC &lt;a href=&quot;http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=12640&quot;&gt;here&lt;/a&gt; and you can read up on the latest release on Scott Guthrie's blog &lt;a href=&quot;http://weblogs.asp.net/scottgu/archive/2008/04/16/asp-net-mvc-source-refresh-preview.aspx&quot;&gt;here&lt;/a&gt;.
&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>mark</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-08:10</id>
    <published>2008-05-08T17:26:00Z</published>
    <updated>2008-05-21T13:48:39Z</updated>
    <category term="Java"/>
    <category term="Ruby"/>
    <category term="jruby"/>
    <category term="rails"/>
    <category term="tomcat"/>
    <category term="warbler"/>
    <link href="http://edge.matrixprojects.com/2008/5/8/deploying-rails-2-0-2-on-tomcat-5-5-x-on-warbler" rel="alternate" type="text/html"/>
    <title>Deploying Rails 2.0.2 on Tomcat 5.5.x with Warbler</title>
<content type="html">
            &lt;p&gt;
			After working with Rails for some time I wanted to determine how to deploy an application on Tomcat. I will discuss how to build a war file that can be used on Tomcat and show pitfalls I experienced along the way. Here are the technologies I am using for this deployment:&lt;br&gt;
		&lt;/p&gt;
		&lt;ul&gt;
			&lt;li&gt;JRuby 1.1.1
			&lt;/li&gt;
			&lt;li&gt;Rails 2.0.2
			&lt;/li&gt;
			&lt;li&gt;Warbler 0.9.5
			&lt;/li&gt;
			&lt;li&gt;MySQL 5.0.4
			&lt;/li&gt;
		&lt;/ul&gt;
		&lt;p&gt;
			To package up my Rails application I am using Warbler as indicated above. This is an alternative to Goldspike but allows a better packaging scheme and an easier configuration. Warble is actually a wrapper to Goldspike and includes these dependencies when you install the gem. You can read more about Warbler &lt;a href=&quot;http://blog.nicksieger.com/articles/2007/09/04/warbler-a-little-birdie-to-introduce-your-rails-app-to-java&quot;&gt;here&lt;/a&gt;.&lt;br&gt;
			&lt;br&gt;
			I will assume you have Rails installed as a gem in JRuby. To make a standalone war file make sure you run this command to freeze Rails in your project:&lt;br&gt;
			&lt;br&gt;
			&lt;span&gt;$ jruby -S rail:freeze:gems&lt;/span&gt;&lt;br&gt;
			&lt;br&gt;
			This will write the standard Rails gem information to your &lt;span&gt;RAILS_ROOT/vendor/rails&lt;/span&gt; directory. Here is how you install Warbler from JRuby:&lt;br&gt;
			&lt;br&gt;
			&lt;span&gt;$ jruby -S gem install warble&lt;/span&gt;&lt;br&gt;
			&lt;br&gt;
			After this is configured switch to the root of your Rails project and execute this command:&lt;br&gt;
			&lt;br&gt;
			&lt;span&gt;$ jruby -S warble&lt;/span&gt;&lt;br&gt;
			&lt;br&gt;
			This will build an exploded war file in a temporary directory within your project. This path can be changed in the &lt;span&gt;warble.rb&lt;/span&gt; file that gets installed. Now create a Warble configuration file by executing this command since we will need it (not sure why this is done after the fact):&lt;br&gt;
			&lt;br&gt;
			&lt;span&gt;$ jruby -S warble config&lt;/span&gt;&lt;br&gt;
			&lt;br&gt;
			After generating a configuration file you should consider updating the JRuby jar file that comes with Warble since you will want the latest version packaged with your war file. If you do not do this Warble will always include the original version of the JRuby jar with your war file. Add the &lt;span&gt;jruby-complete-1.1.1.jar&lt;/span&gt; file which can be obtained &lt;a href=&quot;http://repository.codehaus.org/org/jruby/jruby-complete/1.1.1&quot;&gt;here&lt;/a&gt;) to your &lt;span&gt;RAILS_HOME/lib&lt;/span&gt; directory. Grab the latest Goldspike jar file and place it in your &lt;span&gt;RAILS_HOME/lib&lt;/span&gt; directory. Now add this code to the configuration:&lt;br&gt;
			&lt;br&gt;
			&lt;span&gt;config.java_libs.reject! {&lt;br&gt;
			|lib| lib =~ /jruby-complete|goldspike/&lt;br&gt;
			}&lt;/span&gt;&lt;br&gt;
			&lt;br&gt;
			This allows you to externally package your own versions of JRuby and Goldspike and reject the verions of these jar files that come bundled in the gem directory (&lt;span&gt;$JRUBY_HOME/lib/ruby/gems/1.8/gems/warbler-0.9.5/lib/warbler&lt;/span&gt;).&lt;br&gt;
			&lt;br&gt;
			Also, you can delete the exploded directory and the war file by issuing this command:&lt;br&gt;
			&lt;br&gt;
			&lt;span&gt;$ jruby -S warble war:clean&lt;/span&gt;&lt;br&gt;
			&lt;br&gt;
			From this point you just move the war file to your Tomcat webapps directory and start the server. This is great when everything works. However, I was lucky enough to run into some issues while making this happen.&lt;br&gt;
			&lt;br&gt;
			The first issue occurred when I started Tomcat with the war file I built with Warble (remember that this is using Goldspike under the covers) and got this error message in the Tomcat logs:&lt;br&gt;
			&lt;br&gt;
			&lt;span&gt;&lt;span&gt;“Could not load Rails. See the logs for more details.”&lt;/span&gt;&lt;/span&gt;&lt;br&gt;
			&lt;br&gt;
			Yipee! So when I went to look in the Tomcat logs there was no additional information. Great! It turns out that the stack trace output gets swallowed on a Mac. This is fairly easy to fix (but should be patched by the Goldspike team) by modifying the source code for the goldspike-1.6.1.jar file to emit the full stack trace. Grab the latest Goldspike code from here:&lt;br&gt;
			&lt;span&gt;&lt;br&gt;
			&lt;span&gt;svn checkout&lt;br&gt;
			svn://rubyforge.org/var/svn/jruby-extras/trunk/rails-integration&lt;/span&gt;&lt;/span&gt;&lt;br&gt;
			&lt;br&gt;
			You will have to modify src/main/java/org/jruby/webapp/RailsFactory.java like this:&lt;br&gt;
		&lt;/p&gt;
		&lt;pre&gt;
&lt;span&gt;&lt;br&gt;} catch (RaiseException e) {&lt;br&gt;&lt;span&gt;//added this line so the full stack trace is shown&lt;/span&gt; &lt;span&gt;   &lt;br&gt;e.printStackTrace();&lt;/span&gt;&lt;br&gt;logRubyException(&quot;Failed to load Rails&quot;, e);&lt;br&gt;throw new ServletException(&lt;br&gt;  &quot;Could not load Rails. See the logs for more details.&quot;);&lt;br&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;
		&lt;p&gt;
			After you rebuild the Goldspike jar add it to your RAILS_ROOT/lib. Now re-warble your war file and put it on your Tomcat server. Now you should be able to find your error. In my case I was not making a connection to my database. I was clearly missing gems in my war file. Great!&lt;br&gt;
			&lt;br&gt;
			This really is not too big of a deal. You can explain to Warbler what gems need to be packaged with your war file. Here is a snippet from my warble.rb file that shows how I included the missing gems:&lt;br&gt;
			&lt;br&gt;
			&lt;span&gt;&lt;span&gt;# Gems to be packaged in the webapp. Note that Rails&lt;/span&gt;&lt;br&gt;
			&lt;span&gt;#&lt;/span&gt;&lt;/span&gt; &lt;span&gt;&lt;span&gt;gems are added to this&lt;/span&gt;&lt;/span&gt; &lt;span&gt;&lt;span&gt;list if vendor/rails is not&lt;br&gt;
			#&lt;/span&gt;&lt;/span&gt; &lt;span&gt;&lt;span&gt;present, so be sure to include&lt;/span&gt;&lt;/span&gt; &lt;span&gt;&lt;span&gt;rails if you overwrite&lt;br&gt;
			# the value&lt;/span&gt;&lt;br&gt;
			&lt;span&gt;config.gems = [&quot;activerecord-jdbc-adapter&quot;, &quot;jruby-openssl&quot;, &quot;activerecord-jdbcmysql-adapter&quot;, &quot;jdbc-mysql&quot;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;
			&lt;br&gt;
			Alright re-warble and... still no connectivity. Awesome! This time I was missing my MySQL jar file in my war file and the activerecord-jdbcmysql-adapter gem could not communicate with the implementation jar. To fix this copy your MySQL jar file to the &lt;span&gt;RAILS_ROOT/lib&lt;/span&gt; directory in your Rails project.&lt;br&gt;
			&lt;br&gt;
			One more re-warbling and more good news:&lt;br&gt;
			&lt;br&gt;
			&lt;span&gt;&lt;span&gt;“Rails Error: No :secret given to the #protect_from_forgery call. Set that or use a session store capable of generating its own keys (Cookie Session Store)”&lt;/span&gt;.&lt;/span&gt;&lt;br&gt;
			&lt;br&gt;
			This is a known bug with Goldspike and Rails 2.0. So edit your web.xml file and add the following:&lt;span&gt;&lt;br&gt;&lt;/span&gt;
		&lt;/p&gt;
		&lt;pre&gt;
&lt;span&gt;&amp;lt;context-param&amp;gt;&lt;br&gt;&amp;lt;param-name&amp;gt;jruby.session_store&amp;lt;/param-name&amp;gt;&lt;br&gt;&amp;lt;!-- This value really means let Rails take care &lt;br&gt;        of session store --&amp;gt;&lt;br&gt;&amp;lt;param-value&amp;gt;db&amp;lt;/param-value&amp;gt;&lt;br&gt;&amp;lt;/context-param&amp;gt;&lt;br&gt;&lt;/span&gt;
&lt;/pre&gt;
		&lt;p&gt;
			OK, please just one more re-warbling and deployment to Tomcat and a successful standalone war file has been created and deployed. *birds singing*&lt;br&gt;
			&lt;br&gt;
			I would also suggest increasing the Java memory setting in the JAVA_OPTS of Tomcat to at least -Xmx512m to improve performance. Also keep in mind that Warble is configured to use the production environment for your Rails application so make the appropriate migrations before starting the application. You can make further modification to the Goldspike configuration within the warble.rb file to tweak the pool of Rails runtimes. Once I made these changes it was pretty transparent where I was running my Rails application.&lt;br&gt;
			&lt;br&gt;
			I hope this saves other people time when running into issues. This was kind of a frustrating process since it should be a simple one line deployment. However, I was happy with the end results.
		&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>mark</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-08:9</id>
    <published>2008-05-08T17:24:00Z</published>
    <updated>2008-05-21T13:51:12Z</updated>
    <category term="Java"/>
    <category term="grails"/>
    <category term="groovy"/>
    <link href="http://edge.matrixprojects.com/2008/5/8/migrating-to-grails" rel="alternate" type="text/html"/>
    <title>Migrating to Grails?</title>
<content type="html">
            &lt;p&gt;
	The last time I looked at Grails it was around version 0.4. Now that we are approaching a 1.0 release I wanted to see how much further the Grails team has advanced. I wanted to start by using my existing Spring and Hibernate components from my Java project (I am currently using Stripes for our presentation layer). Benefits for doing this might include:&lt;br&gt;
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Abstracting your Java domain model and services so they are not coupled to Grails.&lt;br&gt;
		&lt;br&gt;
	&lt;/li&gt;
	&lt;li&gt;The ability to continue using your Java services and model objects outside of Web applications or have the ability to integrate with other Web presentation frameworks.&lt;br&gt;
		&lt;br&gt;
	&lt;/li&gt;
	&lt;li&gt;A migration path to Groovy if you want to rid yourself of straight Java altogether. I am sure that there would be massive redux in LOC. For me it was experimenting with the intersection of my experiences with Rails and Java Web development.&lt;br&gt;
	&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
	Does this mean that if you reuse your Hibernate Java domain objects you do not get all of the GORM features like (order.save())? No, your domain objects transparently take full advantage of the mixed in methods in Grails. You might find that the services you are reusing from Spring are mostly passthroughs to DAO that you do not need anymore because of GORM. Here is a trivial example showing how to use a Grails controller to use existing Spring services to perform DAO operations and how to use your Hibernate domain objects directly as GORM objects:
&lt;/p&gt;
&lt;p&gt;
	&lt;br&gt;
&lt;/p&gt;
&lt;pre&gt;
UserService userService  // This is injected in via auto-wiring&lt;br&gt;&lt;br&gt;def index = {&lt;br&gt;&lt;br&gt;  // Using Java Services&lt;br&gt;  def user = userService.find(1, User.class)&lt;br&gt;  println user.firstName&lt;br&gt;  user.firstName = 'Mark'&lt;br&gt;  userService.save user&lt;br&gt;  println User.get(1).firstName&lt;br&gt;&lt;br&gt;  // Using strait Grails&lt;br&gt;  user = User.get(1)&lt;br&gt;  println user.firstName&lt;br&gt;  user.firstName = 'Phil'&lt;br&gt;  user.save()&lt;br&gt;  println User.get(1).firstName&lt;br&gt;}
&lt;/pre&gt;
&lt;p&gt;
	Here is how we took our annotated Hiberate classes so Grails would recognize them:
&lt;/p&gt;
&lt;p&gt;
	Edit your hibernate.cfg.xml file in the GRAILS_APP/grails_app/conf/hibernate directory and add your domain objects:
&lt;/p&gt;
&lt;pre&gt;
&amp;lt;!DOCTYPE hibernate-configuration SYSTEM&lt;br&gt;&quot;http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd&quot;&amp;gt;&lt;br&gt;  &amp;lt;hibernate-configuration&amp;gt;&lt;br&gt;    &amp;lt;session-factory&amp;gt;&lt;br&gt;      &amp;lt;mapping class=&quot;com.meagle.common.bo.Address&quot; /&amp;gt;&lt;br&gt;      &amp;lt;mapping class=&quot;com.meagle.common.bo.User&quot; /&amp;gt;&lt;br&gt;    &amp;lt;/session-factory&amp;gt;&lt;br&gt;  &amp;lt;/hibernate-configuration&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
	In your DataSource.groovy configuration file add the configClass line to your datasource configuration so your annotated Hibernate classes will be recognized by Grails:
&lt;/p&gt;
&lt;pre class=&quot;active4d&quot;&gt;
dataSource {
  pooled = &lt;span class=&quot;BuiltInConstant&quot;&gt;false&lt;/span&gt;
  driverClassName = &lt;span class=&quot;String&quot;&gt;&amp;quot;com.ibm.db2.jcc.DB2Driver&amp;quot;&lt;/span&gt;
  username = &lt;span class=&quot;String&quot;&gt;&amp;quot;meagle&amp;quot;&lt;/span&gt;
  password = &lt;span class=&quot;String&quot;&gt;&amp;quot;******&amp;quot;&lt;/span&gt;
  configClass
     =&lt;span class=&quot;Storage&quot;&gt;org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration&lt;/span&gt;.&lt;span class=&quot;Storage&quot;&gt;class&lt;/span&gt;
}
&lt;/pre&gt;
&lt;p&gt;
	Here is how you expose your existing Spring beans to your Grails application:
&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Edit your GRAILS_APP/grails_app/conf/spring/resources.xml file and add the following line:&lt;br&gt;
		&lt;pre&gt;
&amp;lt;import resource=&quot;applicationContext-core.xml&quot;/&amp;gt;
&lt;/pre&gt;This tells Grails to include your Spring resources (We put our Spring config file in the same directory as the resources.xml file).&lt;br&gt;
		&lt;br&gt;
	&lt;/li&gt;
	&lt;li&gt;Make sure all of your Spring and Hibernate domain objects written in Java are on the classpath. We put ours in a jar file in the GRAILS_APP/lib directory.&lt;br&gt;
		&lt;br&gt;
	&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
	One gotcha here to remember is that your Spring configuration file should not include any definition to a SessionFactory and your services and DAOs that reference a SessionFactory needs to be the name &quot;sessionFactory&quot;. Otherwise, you will end up with multiple session factory objects which is probably not what you want.
&lt;/p&gt;
&lt;p&gt;
	Overall I am very impressed with Grails at this point and would challenge Java developers to take a serious look at this framework. If you are fortunate enough to have IntelliJ 7 then there is decent plugin support for building Grails applications.
&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>mark</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-08:11</id>
    <published>2008-05-08T16:19:00Z</published>
    <updated>2008-05-08T18:20:35Z</updated>
    <category term="java"/>
    <category term="javascript"/>
    <category term="scriptaculous"/>
    <category term="stripes"/>
    <link href="http://edge.matrixprojects.com/2008/5/8/autocomplete-with-stripes-and-scriptaculous" rel="alternate" type="text/html"/>
    <title>Autocomplete with Stripes and Scriptaculous</title>
<content type="html">
            &lt;p&gt;I recently had to implement autocompletion AJAX functionality with &lt;a href=&quot;http://script.aculo.us/&quot;&gt;script.aculous.us&lt;/a&gt; and &lt;a href=&quot;http://stripes.mc4j.org/&quot;&gt;Stripes&lt;/a&gt;.  Once again this AJAX functionality was simple to integrate with Stripes.  This code will return a list of users to select from after the user types more than two characters.&lt;br /&gt;&lt;br /&gt;Here is an example Stripes action that will be responsible for looking up the users and returning a partial page with the unorder list of users.  In this example code the autoCompleteText will capture the text that will be autocompleted.  A Spring bean will be used to call out to service which will use a Hibernate DAO to query for the user list.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;code&quot;&gt;&lt;div class=&quot;codeContent&quot;&gt;&lt;pre class=&quot;code-java&quot;&gt; &lt;span class=&quot;code-keyword&quot;&gt;public class&lt;/span&gt; UserLookupAction &lt;span class=&quot;code-keyword&quot;&gt;implements&lt;/span&gt; ActionBean {&lt;br /&gt;&lt;br /&gt;  String autoCompleteText;&lt;br /&gt;&lt;br /&gt;  UserService userService;&lt;br /&gt;  &lt;span class=&quot;code-keyword&quot;&gt;private&lt;/span&gt; List&amp;lt;user&gt; users;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;code-keyword&quot;&gt;public&lt;/span&gt; Resolution findUsers(){&lt;br /&gt;   &lt;span class=&quot;code-keyword&quot;&gt;this&lt;/span&gt;.users = userService.findUsersByAutocompletion(autoCompleteText);&lt;br /&gt;   &lt;span class=&quot;code-keyword&quot;&gt;return new&lt;/span&gt; ForwardResolution(&lt;span class=&quot;code-quote&quot;&gt;&quot;/pages/useradmin/FindUsers.jsp&quot;&lt;/span&gt;);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @SpringBean&lt;br /&gt;  &lt;span class=&quot;code-keyword&quot;&gt;public void&lt;/span&gt; setUserService(UserService userService) {&lt;br /&gt;   &lt;span class=&quot;code-keyword&quot;&gt;this&lt;/span&gt;.userService = userService;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;code-keyword&quot;&gt;public&lt;/span&gt; String getAutoCompleteText() {return autoCompleteText;}&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;code-keyword&quot;&gt;public void&lt;/span&gt; setAutoCompleteText(String autoCompleteText) {&lt;br /&gt;   &lt;span class=&quot;code-keyword&quot;&gt;this&lt;/span&gt;.autoCompleteText = autoCompleteText;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;code-keyword&quot;&gt;public&lt;/span&gt; List&amp;lt;user&gt; getUsers() {&lt;br /&gt;   return users;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;code-keyword&quot;&gt;public void&lt;/span&gt; setActionBeanContext(ActionBeanContext actionBeanContext);&lt;br /&gt;  &lt;span class=&quot;code-keyword&quot;&gt;public&lt;/span&gt; ActionBeanContext getActionBeanContext() {&lt;span class=&quot;code-keyword&quot;&gt;return this&lt;/span&gt;.actionBeanContext;}&lt;br /&gt; }&lt;br /&gt;&amp;lt;/user&gt;&amp;lt;/user&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt;&lt;br /&gt;This JSP is responsible for making the AJAX calls to the Stripes action above.  The AJAX.Autocompleter code from Scriptaculous does the work when more than two characters are present in the text field named autocomplete.  If there are results they are returned in a partial page as an unordered list (more later).  An animated gif indicator is used to signal the user that the AJAX call is being made and searching for results.  Also, there is a callback method called getSelectionId that is called afterUpdateElement.  This will be used to set the selected user id in a hidden field named autocompleteUserId.  This will be used when editing the user through another Stripes action (UserEditAction).&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;code&quot;&gt;&lt;div class=&quot;codeContent&quot;&gt;&lt;pre class=&quot;code-java&quot;&gt; &amp;lt;style type=&lt;span class=&quot;code-quote&quot;&gt;&quot;text/css&quot;&lt;/span&gt;&amp;gt;&lt;br /&gt;          div.autocomplete {&lt;br /&gt;            position:absolute;&lt;br /&gt;            background-color:white;&lt;br /&gt;            border:1px solid #888;&lt;br /&gt;            margin:0px;&lt;br /&gt;            padding:0px;&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          div.autocomplete ul {&lt;br /&gt;            list-style-type:none;&lt;br /&gt;            margin:0px;&lt;br /&gt;            padding:0px;&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          div.autocomplete ul li.selected { background-color: #ffb;}&lt;br /&gt;&lt;br /&gt;          div.autocomplete ul li {&lt;br /&gt;            list-style-type:none;&lt;br /&gt;            display:block;&lt;br /&gt;            margin:0;&lt;br /&gt;            padding:5px;&lt;br /&gt;            cursor:pointer;&lt;br /&gt;            border-bottom: 1px solid #cbcbcb;&lt;br /&gt;          }&lt;br /&gt; &amp;lt;/style&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;%@ taglib prefix=&lt;span class=&quot;code-quote&quot;&gt;&quot;stripes&quot;&lt;/span&gt; uri=&lt;span class=&quot;code-quote&quot;&gt;&quot;http://stripes.sourceforge.net/stripes.tld&quot;&lt;/span&gt; %&amp;gt;&lt;br /&gt;&lt;br /&gt; &lt;span class=&quot;code-tag&quot;&gt;&amp;lt;stripes:form beanclass=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;com.meagle.web.stripes.action.useradmin.UserEditAction&quot;&lt;/span&gt;&lt;span class=&quot;code-tag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;           User Name:&lt;br /&gt;           &lt;span class=&quot;code-tag&quot;&gt;&amp;lt;stripes:text size=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;30&quot;&lt;/span&gt; &lt;span class=&quot;code-tag&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;autocomplete&quot;&lt;/span&gt; &lt;span class=&quot;code-tag&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;autoCompleteText&quot;&lt;/span&gt;&lt;span class=&quot;code-tag&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;           &lt;span class=&quot;code-tag&quot;&gt;&amp;lt;div id=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;autocomplete_choices&quot;&lt;/span&gt; &lt;span class=&quot;code-tag&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;autocomplete&quot;&lt;/span&gt;&amp;gt;&lt;span class=&quot;code-tag&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;           &lt;span class=&quot;code-tag&quot;&gt;&amp;lt;script type=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;text/javascript&quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;           new Ajax.Autocompleter (&lt;span class=&quot;code-quote&quot;&gt;&quot;autocomplete&quot;&lt;/span&gt;,&lt;br /&gt;           &lt;span class=&quot;code-quote&quot;&gt;&quot;autocomplete_choices&quot;&lt;/span&gt;,&lt;br /&gt;           &lt;span class=&quot;code-quote&quot;&gt;&quot;${pageContext.request.contextPath}/useradmin/UserLookup.action?findUsers=&quot;&lt;/span&gt;,&lt;br /&gt;           {minChars: 2, afterUpdateElement : getSelectionId, indicator: &lt;span class=&quot;code-quote&quot;&gt;'indicator1'&lt;/span&gt;});&lt;br /&gt;&lt;br /&gt;           function getSelectionId(text, li) {&lt;br /&gt;              $(&lt;span class=&quot;code-quote&quot;&gt;'autocompleteUserId'&lt;/span&gt;).value = li.id;&lt;br /&gt;           }&lt;br /&gt;           &lt;span class=&quot;code-tag&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;           &lt;span class=&quot;code-tag&quot;&gt;&amp;lt;stripes:hidden name=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;userId&quot;&lt;/span&gt; &lt;span class=&quot;code-tag&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;autocompleteUserId&quot;&lt;/span&gt;/&amp;gt;&lt;br /&gt;           &lt;span class=&quot;code-tag&quot;&gt;&amp;lt;stripes:submit name=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;editUser&quot;&lt;/span&gt; class=&lt;span&gt;&lt;span class=&quot;code-tag&quot;&gt;class=&lt;/span&gt;&quot;code-quote&quot;&amp;gt;&quot;textBox&quot;&lt;/span&gt; &lt;span class=&quot;code-tag&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;Edit&quot;&lt;/span&gt;/&amp;gt;&lt;br /&gt;&lt;br /&gt;           &lt;span class=&quot;code-tag&quot;&gt;&amp;lt;span id=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;indicator1&quot;&lt;/span&gt; &lt;span class=&quot;code-tag&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;display: none&quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;code-tag&quot;&gt;&lt;br /&gt;           &amp;lt;img width=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;14&quot;&lt;/span&gt; &lt;span class=&quot;code-tag&quot;&gt;height=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;14&quot;&lt;/span&gt;&lt;br /&gt;           &lt;span class=&quot;code-tag&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;${pageContext.request.contextPath}/images/indicator_circle_ball.gif&quot;&lt;/span&gt;&lt;br /&gt;           &lt;span class=&quot;code-tag&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;Working...&quot;&lt;/span&gt; /&amp;gt;&lt;br /&gt;&lt;br /&gt; &lt;span class=&quot;code-tag&quot;&gt;&amp;lt;/stripes:form&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;/pre&gt; &lt;/div&gt;&lt;/div&gt;Finally, this is the partial page that is returned by the UserLookupAction when results are returned.  This simply iterates over the User objects from the Stripes action and creates an unorder list that is stylized with the CSS above.&lt;br /&gt;&lt;div class=&quot;code&quot;&gt;&lt;div class=&quot;codeContent&quot;&gt;&lt;br /&gt;  &lt;pre class=&quot;code-java&quot;&gt; &lt;span class=&quot;code-tag&quot;&gt;&amp;lt;%@ taglib prefix=&lt;span class=&quot;code-quote&quot;&gt;&quot;c&quot;&lt;/span&gt; uri=&lt;span class=&quot;code-quote&quot;&gt;&amp;lt;http://java.sun.com/jsp/jstl/core&amp;gt;&lt;/span&gt; %&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;ul&amp;gt;&lt;br /&gt;         &amp;lt;c:forEach var=&lt;span class=&quot;code-quote&quot;&gt;&quot;user&quot;&lt;/span&gt; items=&lt;span class=&quot;code-quote&quot;&gt;&quot;${ actionBean.users}&quot;&lt;/span&gt;&amp;gt;&lt;br /&gt;            &amp;lt;li id=&lt;span class=&quot;code-quote&quot;&gt;&quot;${user.id}&quot;&lt;/span&gt;&amp;gt;${user.firstName} ${user.lastName}&amp;lt;/li&amp;gt;&lt;br /&gt;         &amp;lt;/c:forEach&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>stonean</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-06:8</id>
    <published>2008-05-06T22:36:00Z</published>
    <updated>2008-05-07T00:24:32Z</updated>
    <category term="Ruby"/>
    <category term="ruby"/>
    <link href="http://edge.matrixprojects.com/2008/5/6/writing-efficient-ruby-code" rel="alternate" type="text/html"/>
    <title>Writing Efficient Ruby Code</title>
<content type="html">
            &lt;p&gt;Writing code is not typically atomic in nature.  Even though a particular method may be, you have to think about the &quot;big picture&quot; in relation to the Module or Class and how it fits within your architecture.  It's easy to lose focus on some of the finer details that could have a negative impact on the performance of your system.&lt;/p&gt;

&lt;p&gt;These type of errors are usually unintentional and it's not that they aren't considered harmful,  they just aren't considered.  To discover some of the things you can do to write better code I recommend picking up a copy of &lt;a href=&quot;http://www.informit.com/store/product.aspx?isbn=0321540034&quot;&gt;&quot;Writing Efficient Ruby Code&quot;&lt;/a&gt; by  Dr. Stefan Kaes.&lt;/p&gt;

&lt;p&gt;The information value in relation to the cost of the book ($12.99) is very good and he does provide excerpts to entice you to buy.  All in all, a nice job done.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>stonean</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-05:7</id>
    <published>2008-05-05T23:09:00Z</published>
    <updated>2008-05-08T18:11:23Z</updated>
    <category term="Ruby"/>
    <category term="rails"/>
    <link href="http://edge.matrixprojects.com/2008/5/5/where-does-the-code-go" rel="alternate" type="text/html"/>
    <title>Where does the code go?</title>
<content type="html">
            &lt;p&gt;Over my time of using Rails I've struggled with the placement of code that does not explicitly belong to a controller, model or view. My Java background probably had a lot to do with my selection of the lib dir for all 'extra' code when I first started.&lt;/p&gt;

&lt;p&gt;Then I learned about plugins and loved the auto require feature. No more requires in my environment.rb. Sweet, but I didn't feel everything qualified as a plugin, or at least to my definition of a plugin. So now I have some code in the plugin directory and some code in the lib dir without any rule on where to look for a particular type of code. Things are starting to get messy.&lt;/p&gt;

&lt;p&gt;I wasn't stopping there. I decided to use helpers for their intent to house &quot;view only&quot; code. This cleaned up my lib directory some but now there are three places to look. You would think that would be enough. Oh no, there's more!&lt;/p&gt;

&lt;p&gt;Rails 2.0 introduced config/initializers and I thought this was great, the answer to my dilemma of lib vs plugin. Code in the initializers dir is loaded automatically like the plugin and it wasn't &quot;way out there&quot; in vendor/plugins so it felt more like a part of the application. Initializers for everything!&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;Those of you who have been coding for a while realize that this is a bad idea. Nothing is for everything. So before I went on my mindless bulk move, I decided I needed to think about this some. This is what I have decided.&lt;/p&gt;

&lt;p&gt;View code goes in the helper. That was easy.&lt;/p&gt;

&lt;p&gt;Plugins should be reserved for generic functionality such as attachment_fu. For every project you write that needs file uploads you can use this same plugin. You don't have to alter the code on a per project basis. It's good to go. So, if you have multiple applications and you find yourself copying code over and over without modifications, it should probably be a plugin.&lt;/p&gt;

&lt;p&gt;In one sense, I consider initializers to be plugins that may need some degree of customization. Project constants are something you use all the time, but they need to be specific for the project. Even though you may reuse a lot of the same constants, odds are some will be specific to the project. Don't separate out the ones that never change to a plugin. This leaves you with two places to look and will cause you headaches.&lt;/p&gt;

&lt;p&gt;Another use for initializers is code that is used throughout your application, but it's not generic enough to be a plugin. A module that maintains your session is a perfect example.&lt;/p&gt;

&lt;p&gt;This leaves the lib directory. This should be reserved for specialized functionality, something only a couple of controllers or models may use and would be &quot;required&quot; only where necessary. Specialized math or string methods come to mind.&lt;/p&gt;

&lt;p&gt;Another, more common use of the lib directory would be to house modules that eliminate repetitive code. For example, say I have multiple models using the same find method like find_recent_posts_by_author. This could be applied to books, pages, comments, etc... I don't want to rewrite this find_recent_posts_by_author method in each of those models. So, I write it once in a module and include it in only the models that need that functionality.&lt;/p&gt;

&lt;p&gt;Consider these guidelines, not strict rules.&lt;/p&gt;

&lt;p&gt;You can also add your own directories and then add them to the Rails load path.  A very common use of this would be the addition of a app/mailers.  Rails mixes these into the app/models and I (among others) don't agree with this approach.&lt;/p&gt;

&lt;p&gt;If you have some other constructs or disagree with any of the above, I'd love to hear your approach.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://edge.matrixprojects.com/">
    <author>
      <name>stonean</name>
    </author>
    <id>tag:edge.matrixprojects.com,2008-05-05:6</id>
    <published>2008-05-05T22:58:00Z</published>
    <updated>2008-05-08T18:11:12Z</updated>
    <category term="Ruby"/>
    <category term="rspec"/>
    <category term="ruby"/>
    <link href="http://edge.matrixprojects.com/2008/5/5/mikey" rel="alternate" type="text/html"/>
    <title>Mikey</title>
<content type="html">
            So, I have a site that synchronizes data with another server. It does this via after_create, after_update and after_delete triggers on some models. All of those triggers use the same method that returns a connection object which has various methods such as:
&lt;br /&gt;&lt;br /&gt;
&lt;pre&gt;
  # SyncServer::connection method
  module SyncServer
    def self.connection
     new_client.connection{|conn| yield conn }
    end
  end

  SyncServer::connection do |conn|
    conn.add_person(person_data)
    # or
    conn.update_person(person_data)
    # or
    conn.delete_person(person_id)
  end
  
  # The following is to demonstrate I could have
  # multiple types of data I'm syncing
  # and therefore multiple methods on the
  # conn object.

  SyncServer::connection do |conn|
    conn.add_other_data(other_data)
    conn.modify_other_data(other_data)
    conn.delete_data(other_id)
  end
&lt;/pre&gt;
&lt;br /&gt;
&lt;p&gt;
While the RSpec tests were running, these triggers were being called which was junking up the SyncServer. This is not cool. In order to avoid this, I needed to be able to disable the synchronization. I definitely didn't want to do this in all my triggers.
&lt;/p&gt;
&lt;p&gt;
Enter Mikey, he's going to replace the conn object I return:
&lt;/p&gt;
&lt;pre&gt;
#
# Because Mikey will eat anything
#
class Mikey
  def method_missing(method, *args)
    true
  end
end
&lt;/pre&gt;
&lt;p&gt;
Now the new conn method:
&lt;/p&gt;
&lt;pre&gt;
  module SyncServer
    def self.connection
     if sync?
      new_client.connection{|conn| yield conn }
     else
      yield Mikey.new
     end
    end
  end
&lt;/pre&gt;
&lt;p&gt;
The sync? method just checks if the class variable do_synchronization is true. I set this class variable to false if the RAILS_ENV == test.

The result is my triggers don't break, the sync server isn't junked up and my tests pass. All good. 
&lt;/p&gt;
          </content>  </entry>
</feed>
