Module: CachedUploads

Extended by:
ActiveSupport::Concern
Defined in:
lib/cached_uploads.rb

Overview

This module enables you to upload files and persist them in a cache in the event that the form must be redisplayed (e.g. due to validation errors). This module also saves the files to their final location when the record is saved.

The controller is still responsible for the overall workflow. It should use the normal ActiveRecord API, with one addition: If the submission is invalid, the controller must call #write_temporary_files.

The controller (or cron task, or a worker process) should also call .clean_temporary_files from time to time. An easy option is to clean up any time an invalid submission is received.

Defined Under Namespace

Modules: ClassMethods

Instance Method Summary collapse

Instance Method Details

#delete_permanent_file(file_attr) ⇒ Object



22
23
24
25
26
27
28
# File 'lib/cached_uploads.rb', line 22

def delete_permanent_file(file_attr)
  config = self.class.cached_uploads[file_attr.to_sym]
  prm_path = send config[:prm_path_method]
  if File.exists?(prm_path)
    File.delete prm_path
  end
end

#write_permanent_file(file_attr) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/cached_uploads.rb', line 41

def write_permanent_file(file_attr)
  config = self.class.cached_uploads[file_attr.to_sym]    
  uploaded_file = send file_attr
  
  if uploaded_file.present?
    # This *won't* execute if we've set the temporary file MD5 attribute instead of the
    # file attribute. It will only execute if we're uploading the file for the first time.
    # (Technically, if both the file and the MD5 are present, this would execute. But that
    # would be an error state.)
    
    prm_file_path = send config[:prm_path_method]
    File.open(prm_file_path, 'wb') do |out_file|
      uploaded_file.rewind
      out_file.write uploaded_file.read
    end
  elsif send(config[:tmp_md5_attr]).present?
    # This executes if we've set the temporary file MD5 attribute instead of the file
    # attribute. This is invoked when the user has submitted invalid data at least once.
    # In which case we've saved the uploaded data to a tempfile on the server. Now the
    # user is resubmitting with correct data. (We know it's correct because
    # #write_permanent_file is triggered by an after_save callback.)
    
    tmp_file_path = send config[:tmp_path_method]
    prm_file_path = send config[:prm_path_method]
    FileUtils.cp tmp_file_path, prm_file_path
  end
end

#write_permanent_file_md5(file_attr) ⇒ Object



30
31
32
33
34
35
36
37
38
39
# File 'lib/cached_uploads.rb', line 30

def write_permanent_file_md5(file_attr)
  config = self.class.cached_uploads[file_attr.to_sym]    
  file = send file_attr
  method = "#{config[:prm_md5_attr]}="
  if file.present? and respond_to?(method)
    file.rewind
    md5 = Digest::MD5.hexdigest(file.read)
    send method, md5
  end
end

#write_temporary_file(file_attr) ⇒ Object

Writes the temporary file, basing its name on the MD5 hash of the uploaded file. Raises if the uploaded file is #blank?.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/cached_uploads.rb', line 71

def write_temporary_file(file_attr)
  config = self.class.cached_uploads[file_attr.to_sym]
  file = send file_attr
  
  if file.present?
    # Read the uploaded file, calc its MD5, and write the MD5 instance variable.
    file.rewind
    md5 = Digest::MD5.hexdigest(file.read)
    send "#{config[:tmp_md5_attr]}=", md5
    
    # Write the temporary file, using its MD5 hash to generate the filename.
    file.rewind
    File.open(send(config[:tmp_path_method]), 'wb') do |out_file|
      out_file.write file.read
    end
  else
    raise "Called #write_temporary_file(:#{file_attr}), but ##{file_attr} was not present."
  end
end

#write_temporary_filesObject

Saves any configured temporary files. Controllers should call this method when an invalid submission is received. Does not save a temporary file if the file itself had a validation error.



94
95
96
97
98
99
100
# File 'lib/cached_uploads.rb', line 94

def write_temporary_files
  cached_uploads.each_key do |file_attr|
    if errors[:file].empty? and send(file_attr).present?
      write_temporary_file file_attr
    end
  end
end