Class: UploadCache
- Inherits:
-
Object
- Object
- UploadCache
- Defined in:
- lib/upload_cache.rb
Defined Under Namespace
Modules: HtmlSafe, WeakReference
Constant Summary collapse
- Version =
'2.2.0'
- Readme =
<<-__ NAME upload_cache.rb DESCRIPTION a small utility library to facility caching http file uploads between form validation failures. designed for rails, but usable anywhere. USAGE in the controller def upload @upload_cache = UploadCache.for(params, :upload) @record = Model.new(params) if request.get? render and return end if request.post? @record.save! @upload_cache.clear! end end in the view <input type='file' name='upload /> <%= @upload_cache.hidden %> <!-- optionally, you can show any uploaded upload --> <% if url = @upload_cache.url %> you already uploaded: <img src='<%= raw url %>' /> <% end %> in a rake task UploadCache.clear! ### nuke old files once per day upload_caches ***does this automatically*** at_exit{}, but you can still run it manually if you like. __
- UUIDPattern =
%r/^[a-zA-Z0-9-]+$/io
- Age =
60 * 60 * 24
- IOs =
{}
Instance Attribute Summary collapse
-
#basename ⇒ Object
Returns the value of attribute basename.
-
#cache_key ⇒ Object
Returns the value of attribute cache_key.
-
#default_path ⇒ Object
Returns the value of attribute default_path.
-
#default_url ⇒ Object
Returns the value of attribute default_url.
-
#dirname ⇒ Object
Returns the value of attribute dirname.
-
#io ⇒ Object
Returns the value of attribute io.
-
#key ⇒ Object
Returns the value of attribute key.
-
#name ⇒ Object
Returns the value of attribute name.
-
#options ⇒ Object
Returns the value of attribute options.
-
#path ⇒ Object
Returns the value of attribute path.
-
#value ⇒ Object
Returns the value of attribute value.
Class Method Summary collapse
- .cache(params, *args) ⇒ Object (also: for)
- .cache_key_for(key) ⇒ Object
- .cleanname(path) ⇒ Object
- .clear!(options = {}) ⇒ Object
- .current_upload_cache_for(params, key, options) ⇒ Object
- .default ⇒ Object
- .default_upload_cache_for(params, key, options) ⇒ Object
- .finalizer(object_id) ⇒ Object
- .name_for(key, &block) ⇒ Object
- .prefix(*value) ⇒ Object
- .prefix=(value) ⇒ Object
- .previous_upload_cache_for(params, key, options) ⇒ Object
- .rewind(io, &block) ⇒ Object
- .root ⇒ Object
- .root=(root) ⇒ Object
- .tmpdir(&block) ⇒ Object
- .turd? ⇒ Boolean
- .update_params(params, key, value) ⇒ Object
- .url ⇒ Object
- .url=(url) ⇒ Object
- .version ⇒ Object
Instance Method Summary collapse
- #blank? ⇒ Boolean
- #clear!(&block) ⇒ Object (also: #clear)
- #hidden ⇒ Object
-
#initialize(key, *args) ⇒ UploadCache
constructor
A new instance of UploadCache.
- #input ⇒ Object
- #inspect ⇒ Object
- #raw(*args) ⇒ Object
- #to_s ⇒ Object
- #url ⇒ Object
Constructor Details
#initialize(key, *args) ⇒ UploadCache
Returns a new instance of UploadCache.
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
# File 'lib/upload_cache.rb', line 356 def initialize(key, *args) @options = Map.(args) @key = key @cache_key = UploadCache.cache_key_for(@key) @name = UploadCache.name_for(@cache_key) path = args.shift || @options[:path] default = Map.for(@options[:default]) @default_url = default[:url] || @options[:default_url] || UploadCache.default.url @default_path = default[:path] || @options[:default_path] || UploadCache.default.path if path @path = path @dirname, @basename = File.split(@path) @value = File.join(File.basename(@dirname), @basename).strip else @path = nil @value = nil end if @path or @default_path @io = open(@path || @default_path, 'rb') IOs[object_id] = @io.fileno ObjectSpace.define_finalizer(self, UploadCache.method(:finalizer).to_proc) @io.send(:extend, WeakReference) @io.upload_cache = self end end |
Instance Attribute Details
#basename ⇒ Object
Returns the value of attribute basename.
348 349 350 |
# File 'lib/upload_cache.rb', line 348 def basename @basename end |
#cache_key ⇒ Object
Returns the value of attribute cache_key.
343 344 345 |
# File 'lib/upload_cache.rb', line 343 def cache_key @cache_key end |
#default_path ⇒ Object
Returns the value of attribute default_path.
352 353 354 |
# File 'lib/upload_cache.rb', line 352 def default_path @default_path end |
#default_url ⇒ Object
Returns the value of attribute default_url.
351 352 353 |
# File 'lib/upload_cache.rb', line 351 def default_url @default_url end |
#dirname ⇒ Object
Returns the value of attribute dirname.
347 348 349 |
# File 'lib/upload_cache.rb', line 347 def dirname @dirname end |
#io ⇒ Object
Returns the value of attribute io.
350 351 352 |
# File 'lib/upload_cache.rb', line 350 def io @io end |
#key ⇒ Object
Returns the value of attribute key.
342 343 344 |
# File 'lib/upload_cache.rb', line 342 def key @key end |
#name ⇒ Object
Returns the value of attribute name.
345 346 347 |
# File 'lib/upload_cache.rb', line 345 def name @name end |
#options ⇒ Object
Returns the value of attribute options.
344 345 346 |
# File 'lib/upload_cache.rb', line 344 def @options end |
#path ⇒ Object
Returns the value of attribute path.
346 347 348 |
# File 'lib/upload_cache.rb', line 346 def path @path end |
#value ⇒ Object
Returns the value of attribute value.
349 350 351 |
# File 'lib/upload_cache.rb', line 349 def value @value end |
Class Method Details
.cache(params, *args) ⇒ Object Also known as: for
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/upload_cache.rb', line 199 def cache(params, *args) params_map = Map.for(params) = Map.(args) key = Array([:key] || args).flatten.compact key = [:upload] if key.empty? upload_cache = ( current_upload_cache_for(params_map, key, ) or previous_upload_cache_for(params_map, key, ) or default_upload_cache_for(params_map, key, ) ) value = params_map.get(key) update_params(params, key, value) upload_cache end |
.cache_key_for(key) ⇒ Object
124 125 126 127 128 |
# File 'lib/upload_cache.rb', line 124 def cache_key_for(key) key.clone.tap do |cache_key| cache_key[-1] = "#{ cache_key[-1] }_upload_cache" end end |
.cleanname(path) ⇒ Object
119 120 121 122 |
# File 'lib/upload_cache.rb', line 119 def cleanname(path) basename = File.basename(path.to_s) CGI.unescape(basename).gsub(%r/[^0-9a-zA-Z_@)(~.-]/, '_').gsub(%r/_+/,'_') end |
.clear!(options = {}) ⇒ Object
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/upload_cache.rb', line 140 def clear!( = {}) return if UploadCache.turd? glob = File.join(root, '*') age = Integer([:age] || ['age'] || Age) since = [:since] || ['since'] || Time.now Dir.glob(glob) do |entry| begin next unless test(?d, entry) next unless File.basename(entry) =~ UUIDPattern files = Dir.glob(File.join(entry, '**/**')) all_files_are_old = files.all? do |file| begin stat = File.stat(file) age = since - stat.atime age >= Age rescue false end end FileUtils.rm_rf(entry) if all_files_are_old rescue next end end end |
.current_upload_cache_for(params, key, options) ⇒ Object
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/upload_cache.rb', line 243 def current_upload_cache_for(params, key, ) upload = params.get(key) if upload.respond_to?(:upload_cache) and upload.upload_cache return upload.upload_cache end if upload.respond_to?(:read) tmpdir do |tmp| original_basename = [:original_path, :original_filename, :path, :filename]. map{|msg| upload.send(msg) if upload.respond_to?(msg)}.compact.first basename = cleanname(original_basename) path = File.join(tmp, basename) copied = false rewind(upload) do src = upload.path dst = path strategies = [ proc{ `ln -f #{ src.inspect } #{ dst.inspect } || cp -f #{ src.inspect } #{ dst.inspect }`}, proc{ FileUtils.ln(src, dst) }, proc{ FileUtils.cp(src, dst) }, proc{ open(dst, 'wb'){|fd| fd.write(upload.read)} } ] FileUtils.rm_f(dst) strategies.each do |strategy| strategy.call rescue nil break if((copied = test(?e, dst))) end end raise("failed to copy #{ upload.path.inspect } -> #{ path.inspect }") unless copied upload_cache = UploadCache.new(key, path, ) params.set(key, upload_cache.io) return upload_cache end end nil end |
.default ⇒ Object
191 192 193 |
# File 'lib/upload_cache.rb', line 191 def default @default ||= Map[:url, nil, :path, nil] end |
.default_upload_cache_for(params, key, options) ⇒ Object
315 316 317 318 319 |
# File 'lib/upload_cache.rb', line 315 def default_upload_cache_for(params, key, ) upload_cache = UploadCache.new(key, ) params.set(key, upload_cache.io) return upload_cache end |
.finalizer(object_id) ⇒ Object
130 131 132 133 134 135 |
# File 'lib/upload_cache.rb', line 130 def finalizer(object_id) if fd = IOs[object_id] ::IO.for_fd(fd).close rescue nil IOs.delete(object_id) end end |
.name_for(key, &block) ⇒ Object
178 179 180 181 182 183 184 |
# File 'lib/upload_cache.rb', line 178 def name_for(key, &block) if block @name_for = block else defined?(@name_for) ? @name_for[key] : [prefix, *Array(key)].compact.join('.') end end |
.prefix(*value) ⇒ Object
186 187 188 189 |
# File 'lib/upload_cache.rb', line 186 def prefix(*value) @prefix = value.shift if value @prefix end |
.prefix=(value) ⇒ Object
195 196 197 |
# File 'lib/upload_cache.rb', line 195 def prefix=(value) @prefix = value end |
.previous_upload_cache_for(params, key, options) ⇒ Object
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/upload_cache.rb', line 293 def previous_upload_cache_for(params, key, ) upload = params.get(key) if upload.respond_to?(:upload_cache) and upload.upload_cache return upload.upload_cache end upload = params.get(cache_key_for(key)) if upload dirname, basename = File.split(File.(upload)) relative_dirname = File.basename(dirname) relative_basename = File.join(relative_dirname, basename) path = root + '/' + relative_basename upload_cache = UploadCache.new(key, path, ) params.set(key, upload_cache.io) return upload_cache end nil end |
.rewind(io, &block) ⇒ Object
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
# File 'lib/upload_cache.rb', line 321 def rewind(io, &block) begin pos = io.pos io.flush io.rewind rescue nil end begin block.call ensure begin io.pos = pos rescue nil end end end |
.root ⇒ Object
79 80 81 82 83 84 85 86 87 |
# File 'lib/upload_cache.rb', line 79 def root @root ||= ( if defined?(Rails.root) and Rails.root File.join(Rails.root, 'public', UploadCache.url) else Dir.tmpdir end ) end |
.root=(root) ⇒ Object
89 90 91 |
# File 'lib/upload_cache.rb', line 89 def root=(root) @root = File.(root) end |
.tmpdir(&block) ⇒ Object
108 109 110 111 112 113 114 115 116 117 |
# File 'lib/upload_cache.rb', line 108 def tmpdir(&block) tmpdir = File.join(root, uuid) if block FileUtils.mkdir_p(tmpdir) block.call(tmpdir) else tmpdir end end |
.turd? ⇒ Boolean
174 175 176 |
# File 'lib/upload_cache.rb', line 174 def turd? @turd ||= !!ENV['UPLOAD_CACHE_TURD'] end |
.update_params(params, key, value) ⇒ Object
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/upload_cache.rb', line 220 def update_params(params, key, value) key = Array(key).flatten leaf = key.pop path = key node = params until path.empty? key = path.shift case node when Array index = Integer(key) break unless node[index] node = node[index] else break unless node.has_key?(key) node = node[key] end end node[leaf] = value end |
.url ⇒ Object
65 66 67 68 69 70 71 72 73 |
# File 'lib/upload_cache.rb', line 65 def url @url ||= ( if defined?(Rails.root) and Rails.root '/system/upload_cache' else "file:/#{ root }" end ) end |
.url=(url) ⇒ Object
75 76 77 |
# File 'lib/upload_cache.rb', line 75 def url=(url) @url = '/' + Array(url).join('/').squeeze('/').sub(%r|^/+|, '').sub(%r|/+$|, '') end |
.version ⇒ Object
60 61 62 |
# File 'lib/upload_cache.rb', line 60 def version UploadCache::Version end |
Instance Method Details
#blank? ⇒ Boolean
413 414 415 |
# File 'lib/upload_cache.rb', line 413 def blank? @path.blank? end |
#clear!(&block) ⇒ Object Also known as: clear
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/upload_cache.rb', line 451 def clear!(&block) result = block ? block.call(@path) : nil unless UploadCache.turd? begin FileUtils.rm_rf(@dirname) if test(?d, @dirname) rescue nil ensure @io.close rescue nil IOs.delete(object_id) Thread.new{ UploadCache.clear! } end end result end |
#hidden ⇒ Object
430 431 432 |
# File 'lib/upload_cache.rb', line 430 def hidden raw("<input type='hidden' name='#{ @name }' value='#{ @value }' class='upload_cache hidden' />") if @value end |
#input ⇒ Object
434 435 436 |
# File 'lib/upload_cache.rb', line 434 def input raw("<input type='file' name='#{ @name }' class='upload_cache input' />") end |
#inspect ⇒ Object
404 405 406 407 408 409 410 411 |
# File 'lib/upload_cache.rb', line 404 def inspect { UploadCache.name => { :key => key, :cache_key => key, :name => name, :path => path, :io => io } }.inspect end |
#raw(*args) ⇒ Object
443 444 445 446 447 448 449 |
# File 'lib/upload_cache.rb', line 443 def raw(*args) string = args.join unless string.respond_to?(:html_safe) string.extend(HtmlSafe) end string.html_safe end |
#to_s ⇒ Object
426 427 428 |
# File 'lib/upload_cache.rb', line 426 def to_s url end |
#url ⇒ Object
417 418 419 420 421 422 423 424 |
# File 'lib/upload_cache.rb', line 417 def url if @value File.join(UploadCache.url, @value) else @default_url ? @default_url : nil #defined?(@placeholder) ? @placeholder : nil end end |