Unobtrusive Upload Progress for Rails with Mongrel
JavaScript Ruby
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
javascripts
lib
tasks
test
MIT-LICENSE
README.rdoc
Rakefile
TODO
init.rb
install.rb
uninstall.rb

README.rdoc

Unobtrusive Upload Progress for Rails with Mongrel

Upload progress is messy, especially when you want to handle validations errors as well. This plugin aims to provide a transparent way to add upload progress to your forms.

Unobtrusive

This plugin tries to be as unobtrusive as possible. It's pretty easy to setup; just add two mongrel handlers and include a JavaScript library. Your forms will not break when the progress monitor isn't available and uploads still work when the user has turned of JavaScript.

Requirements

This plugin depends on Prototype.js 1.6 and Mongrel. If you're using Mongrel Cluster the application tmp directory needs to be on a shared volume with flock capability. This is most likely the case if all your Mongrel instances run on the same (Unix-like) machine.

Will it work for me?

As long as you deploy your application using Mongrel (or a cluster of Mongrels) and your forms are pretty basic (re-rending the same form on validation errors, including all error messages inside the form and redirecting on success as Rails scaffolding does) this plugin will be fine for you. Otherwise the you'll probably need to tweak some of the included JavaScript code.

Installation

This code is packaged as a Rails plugin;

ruby script/plugin install git://github.com/remvee/unobtrusive_upload_progress.git

The installation hook will copy a JavaScript file into your public/javascripts directory.

Mongrel configuration

After installing the plugin, you'll need to setup a Mongrel configuration script to add the following uri mappings:

uri '/', :handler => ::UploadProgress::MonitorHandler.new, :in_front => true
uri '/upload_progress', :handler => ::UploadProgress::StatusHandler.new

This script should be included using the -S switch when starting mongrel_rails or by adding something like:

config_script: config/mongrel_config.rb

to you mongrel-cluster configuration.

Layout

Add the following to the head section of HTML in your layouts:

<%= javascript_include_tag 'upload_progress' if UploadProgress.monitor_loaded? %>

The JavaScript library will only be included when the mongrel handlers are loaded.

How does it work?

When one of your pages is loaded into the browser, the provided JavaScript will look for a form on the page which is a candidate for an upload progress bar; it has encoding type multipart/form-data and an input tag of type file. If such a form is found an iframe is added. The form is altered slightly by adding an extra parameter with an unique identifier to the form action, setting the form target to the introduced iframe and hooking up a handler to the submit event.

When the user hits the submit button the submit event handler kicks off a series of Ajax requests which use the same identifier as the one added to the form action to fetch the status of the upload form the status handler in Mongrel. These updates are used to update the progress bar.

Meanwhile the monitor handler in Mongrel should have detected an upload because it saw a request pass with the special parameter. It will record progress for the associated identifier to be served by the status handler in JSON format.

When the upload is finished the iframe will be examined. When the location is different than the form action the user submitted, the upload is considered successful (typical redirect on successful update/create action) and the top frame location is replaced by the one the iframe got. When the location didn't change there's probably an validation error visible in the form and the form contents (innerHTML) in the iframe is copied into the visible form in the top frame.

Customization

The following parameters can be tweaked:

UploadProgress.progress_directory

Directory used to store progress information. Defaults to “RAILS_ROOT/tmp/upload_progress”.

UploadProgress.sweeper_interval

Interval in seconds for stale progress info sweeper. Defaults to “1.hour”.

UploadProgress.param_name

Parameter name used to flag and identify an upload. *Be careful*, if you change this you'll also need to change the JavaScript library. Defaults to “upload_progress_id”.

Progress bar

The default progress bar is probably not what you want but easily altered. The JavaScript library has three callbacks: start, update and finish. The first allows you to make your progress bar visible, the second handles updating it and the last removes it again.

Here's a very basic example:

if (typeof UploadProgress != 'undefined') {
  UploadProgress.register({
    start: function() {
      $('progress').setStyle({display: 'block'})
    },
    update: function(status) {
      $('progress').innerHTML = status.remaining +
                                " bytes remaining of " +
                                status.total
    },
    finish: function() {
      $('progress').setStyle({display: 'hidden'})
    }
  })
}

Slowing down Mongrel for testing

Upload progress is nice but in a development environment you probably have near to unlimited bandwidth between your browser and your application. Here's a hack to slow down Mongrel:

class Mongrel::HttpRequest
  alias _update_request_progress update_request_progress
  def update_request_progress(*args)
    sleep 0.5
    _update_request_progress(*args)
  end
end

Place it in you mongrel_config.rb script temporarily or put:

if RAILS_ENV == 'development'
  ..
end

around it if you like your Mongrel to encumbered in development mode.

Copyright © 2008 R.W. van 't Veer, released under the MIT license