Module: ActionController::UploadProgress

Defined in:
lib/action_controller/upload_progress.rb

Overview

THIS IS AN EXPERIMENTAL FEATURE

Which means that it doesn’t yet work on all systems. We’re still working on full compatibility. It’s thus not advised to use this unless you’ve verified it to work fully on all the systems that is a part of your environment. Consider this an extended preview.

Action Pack Upload Progress for multipart uploads

The UploadProgress module aids in the process of viewing an Ajax driven upload status when working with multipart forms. It offers a macro that will prepare an action for handling the cleanup of the Ajax updating including passing the redirect URL and custom parameters to the Javascript finish handler.

UploadProgress is available for all multipart uploads when the upload_status_for macro is called in one of your controllers.

The progress is stored as an UploadProgress::Progress object in the session and is accessible in the controller and view with the upload_progress method.

For help rendering the UploadProgress enabled form and supported elements, see ActionView::Helpers::UploadProgressHelper.

Automatic updating on upload actions

class DocumentController < ApplicationController   
  upload_status_for  :create

  def create
    # ... Your document creation action
  end
end

The upload_status_for macro will override the rendering of the action passed if upload_id is found in the query string. This allows for default behavior if Javascript is disabled. If you are tracking the upload progress then create will now return the cleanup scripts that will terminate the polling of the upload status.

Customized status rendering

class DocumentController < ApplicationController   
  upload_status_for  :create, :status => :custom_status

  def create
    # ... Your document creation action
  end

  def custom_status
    # ... Override this action to return content to be replaced in
    # the status container
    render :inline => "<%= upload_progress.completed_percent rescue 0 %> % complete", :layout => false
end

The default status action is upload_status. The results of this action are added used to replace the contents of the HTML elements defined in upload_status_tag. Within upload_status, you can load the Progress object from the session with the upload_progress method and display your own results.

Completion of the upload status updating occurs automatically with an after_filter call to finish_upload_status. Because the upload must be posted into a hidden IFRAME to enable Ajax updates during the upload, finish_upload_status overwrites the results of any previous render or redirect_to so it can render the necessary Javascript that will properly terminate the status updating loop, trigger the completion callback or redirect to the appropriate URL.

Basic Example (View):

<%= form_tag_with_upload_progress({:action => 'create'}, {:finish => 'alert("Document Uploaded")'}) %>
<%= upload_status_tag %>
<%= file_field 'document', 'file' %>
<%= end_form_tag %>

Basic Example (Controller):

class DocumentController < ApplicationController
  upload_status_for :create

  def create
    @document = Document.create(params[:document])
  end
end

Extended Example (View):

<%= form_tag_with_upload_progress({:action => 'create'}, {}, {:action => :custom_status}) %>
<%= upload_status_tag %>
<%= file_field 'document', 'file' %>
<%= submit_tag "Upload" %>
<%= end_form_tag %>

<%= form_tag_with_upload_progress({:action => 'add_preview'}, {:finish => 'alert(arguments[0])'}, {:action => :custom_status})  %>
<%= upload_status_tag %>
<%= submit_tag "Upload" %>
<%= file_field 'preview', 'file' %>
<%= end_form_tag %>

Extended Example (Controller):

class DocumentController < ApplicationController
  upload_status_for :add_preview, :create, {:status => :custom_status}

  def add_preview
   @document = Document.find(params[:id])
   @document.preview = Preview.create(params[:preview])
   if @document.save
     finish_upload_status "'Preview added'"
   else
     finish_upload_status "'Preview not added'"
   end
  end

 def create
   @document = Document.new(params[:document])

   upload_progress.message = "Processing document..."
   session.update

   @document.save
   redirect_to :action => 'show', :id => @document.id
 end

 def custom_status
   render :inline => '<%= upload_progress_status %> <div>Updated at <%= Time.now %></div>', :layout => false
 end

Defined Under Namespace

Modules: ClassMethods Classes: Progress

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.append_features(base) ⇒ Object

:nodoc:



135
136
137
138
139
# File 'lib/action_controller/upload_progress.rb', line 135

def self.append_features(base) #:nodoc:
  super
  base.extend(ClassMethods)
  base.helper_method :upload_progress, :next_upload_id, :last_upload_id, :current_upload_id
end

Instance Method Details

#current_upload_idObject

Returns the upload_id from the query parameters or if it cannot be found in the query parameters, then return the last_upload_id



255
256
257
# File 'lib/action_controller/upload_progress.rb', line 255

def current_upload_id
  params[:upload_id] or last_upload_id
end

#finish_upload_status(client_js_argument = '') ⇒ Object

Overwrites the body rendered if the upload comes from a form that tracks the progress of the upload. After clearing the body and any redirects, this method then renders the helper finish_upload_status

This method only needs to be called if you wish to pass a javascript parameter to your finish event handler that you optionally define in form_with_upload_progress

Parameter:

client_js_argument

a string containing a Javascript expression that will be evaluated and passed to your finish handler of form_tag_with_upload_progress.

You can pass a String, Number or Boolean.

Strings

Strings contain Javascript code that will be evaluated on the client. If you wish to pass a string to the client finish callback, you will need to include quotes in the client_js_argument you pass to this method.

Example

finish_upload_status("\"Finished\"")
finish_upload_status("'Finished #{@document.title}'")
finish_upload_status("{success: true, message: 'Done!'}")
finish_upload_status("function() { alert('Uploaded!'); }")

Numbers / Booleans

Numbers and Booleans can either be passed as Number objects or string versions of number objects as they are evaluated by Javascript the same way as in Ruby.

Example

finish_upload_status(0)
finish_upload_status(@document.file.size)
finish_upload_status("10")

Nil

To pass nil to the finish callback, use a string “undefined”

Example

finish_upload_status(@message || "undefined")

Redirection

If you action performs a redirection then finish_upload_status will recognize the redirection and properly create the Javascript to perform the redirection in the proper location.

It is possible to redirect and pass a parameter to the finish callback.

Example

redirect_to :action => 'show', :id => @document.id
finish_upload_status("'Redirecting you to your new file'")


221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/action_controller/upload_progress.rb', line 221

def finish_upload_status(client_js_argument='')
  if not @rendered_finish_upload_status and params[:upload_id]
    @rendered_finish_upload_status = true

    erase_render_results
    location = erase_redirect_results || ''

    ## TODO determine if #inspect is the appropriate way to marshall values
    ## in inline templates

    template = "<%= finish_upload_status({"
    template << ":client_js_argument => #{client_js_argument.inspect}, "
    template << ":redirect_to => #{location.to_s.inspect}, "
    template << "}) %>"

    render({ :inline => template, :layout => false })
  end
end

#last_upload_idObject

Either returns the last saved upload_id or looks in the session for the last used upload_id and saves it as the intance variable @upload_id



249
250
251
# File 'lib/action_controller/upload_progress.rb', line 249

def last_upload_id
  @upload_id ||= ((session[:uploads] || {}).keys.map{|k| k.to_i}.sort.last || 0).to_s
end

#next_upload_idObject

Returns and saves the next unique upload_id in the instance variable @upload_id



242
243
244
# File 'lib/action_controller/upload_progress.rb', line 242

def next_upload_id
  @upload_id = last_upload_id.succ
end

#upload_progress(upload_id = nil) ⇒ Object

Get the UploadProgress::Progress object for the supplied upload_id from the session. If no upload_id is given, then use the current_upload_id

If an UploadProgress::Progress object cannot be found, a new instance will be returned with total_bytes == 0, started? == false, and finished? == true.



265
266
267
268
# File 'lib/action_controller/upload_progress.rb', line 265

def upload_progress(upload_id = nil)
  upload_id ||= current_upload_id
  session[:uploads] && session[:uploads][upload_id] || UploadProgress::Progress.new(0)
end