uploadcolumn 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/CHANGELOG +123 -0
- data/LICENSE +22 -0
- data/README.rdoc +206 -0
- data/Rakefile +90 -0
- data/VERSION +1 -0
- data/init.rb +15 -0
- data/lib/upload_column/active_record_extension.rb +154 -0
- data/lib/upload_column/configuration.rb +49 -0
- data/lib/upload_column/magic_columns.rb +50 -0
- data/lib/upload_column/manipulators/image_science.rb +86 -0
- data/lib/upload_column/manipulators/rmagick.rb +75 -0
- data/lib/upload_column/rails/action_controller_extension.rb +61 -0
- data/lib/upload_column/rails/asset_tag_extension.rb +17 -0
- data/lib/upload_column/rails/upload_column_helper.rb +45 -0
- data/lib/upload_column/sanitized_file.rb +176 -0
- data/lib/upload_column/uploaded_file.rb +299 -0
- data/lib/upload_column.rb +12 -0
- data/spec/active_record_extension_spec.rb +514 -0
- data/spec/custom_matchers.rb +148 -0
- data/spec/fixtures/animated.gif +0 -0
- data/spec/fixtures/animated_solarized.gif +0 -0
- data/spec/fixtures/invalid-image.jpg +1 -0
- data/spec/fixtures/kerb.jpg +0 -0
- data/spec/fixtures/kerb_solarized.jpg +0 -0
- data/spec/fixtures/netscape.gif +0 -0
- data/spec/fixtures/skanthak.png +0 -0
- data/spec/image_science_manipulator_spec.rb +195 -0
- data/spec/integration_spec.rb +668 -0
- data/spec/magic_columns_spec.rb +120 -0
- data/spec/rmagick_manipulator_spec.rb +186 -0
- data/spec/sanitized_file_spec.rb +496 -0
- data/spec/spec_helper.rb +90 -0
- data/spec/upload_column_spec.rb +65 -0
- data/spec/uploaded_file_spec.rb +1053 -0
- metadata +108 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
module UploadColumn
|
2
|
+
module MagicColumns
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
super
|
6
|
+
base.send :alias_method_chain, :set_upload_column, :magic_columns
|
7
|
+
base.send :alias_method_chain, :set_upload_column_temp, :magic_columns
|
8
|
+
base.send :alias_method_chain, :save_uploaded_files, :magic_columns
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_upload_column_with_magic_columns(name, file)
|
12
|
+
set_upload_column_without_magic_columns(name, file)
|
13
|
+
evaluate_magic_columns_for_upload_column(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_upload_column_temp_with_magic_columns(name, path)
|
17
|
+
set_upload_column_temp_without_magic_columns(name, path)
|
18
|
+
evaluate_magic_columns_for_upload_column(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def save_uploaded_files_with_magic_columns
|
22
|
+
save_uploaded_files_without_magic_columns
|
23
|
+
self.class.reflect_on_upload_columns.each do |name, column|
|
24
|
+
evaluate_magic_columns_for_upload_column(name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def evaluate_magic_columns_for_upload_column(name)
|
31
|
+
|
32
|
+
self.class.column_names.each do |column_name|
|
33
|
+
|
34
|
+
statement, predicate = column_name.split('_', 2)
|
35
|
+
|
36
|
+
if statement and predicate and name.to_s == statement and not self.read_attribute(column_name.to_sym)
|
37
|
+
uploaded_file = self.send(:get_upload_column, name.to_sym)
|
38
|
+
|
39
|
+
self.write_attribute(column_name.to_sym, handle_predicate(uploaded_file, predicate))
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def handle_predicate(uploaded_file, predicate)
|
46
|
+
return uploaded_file.send(predicate.to_sym) if uploaded_file.respond_to?(predicate.to_sym)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module UploadColumn
|
2
|
+
module Manipulators
|
3
|
+
|
4
|
+
module ImageScience
|
5
|
+
|
6
|
+
attr_reader :width, :height
|
7
|
+
|
8
|
+
def load_manipulator_dependencies #:nodoc:
|
9
|
+
require 'image_science'
|
10
|
+
end
|
11
|
+
|
12
|
+
def process!(instruction)
|
13
|
+
if instruction.to_s =~ /^c(\d+x\d+)/
|
14
|
+
crop_resized!($1)
|
15
|
+
elsif instruction.to_s =~ /\d+x\d+/
|
16
|
+
resize!(instruction)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Resize the image so that it will not exceed the dimensions passed
|
21
|
+
# via geometry, geometry should be a string, formatted like '200x100' where
|
22
|
+
# the first number is the height and the second is the width
|
23
|
+
def resize!( geometry )
|
24
|
+
::ImageScience.with_image(self.path) do |img|
|
25
|
+
width, height = extract_dimensions(img.width, img.height, geometry)
|
26
|
+
img.resize( width, height ) do |file|
|
27
|
+
file.save( self.path )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Resize and crop the image so that it will have the exact dimensions passed
|
33
|
+
# via geometry, geometry should be a string, formatted like '200x100' where
|
34
|
+
# the first number is the height and the second is the width
|
35
|
+
def crop_resized!( geometry )
|
36
|
+
::ImageScience.with_image(self.path) do |img|
|
37
|
+
new_width, new_height = geometry.split('x').map{|i| i.to_i }
|
38
|
+
|
39
|
+
width, height = extract_dimensions_for_crop(img.width, img.height, geometry)
|
40
|
+
x_offset, y_offset = extract_placement_for_crop(width, height, geometry)
|
41
|
+
|
42
|
+
img.resize( width, height ) do |i2|
|
43
|
+
|
44
|
+
i2.with_crop( x_offset, y_offset, new_width + x_offset, new_height + y_offset) do |file|
|
45
|
+
file.save( self.path )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def extract_dimensions(width, height, new_geometry, type = :resize)
|
54
|
+
new_width, new_height = convert_geometry(new_geometry)
|
55
|
+
|
56
|
+
aspect_ratio = width.to_f / height.to_f
|
57
|
+
new_aspect_ratio = new_width / new_height
|
58
|
+
|
59
|
+
if (new_aspect_ratio > aspect_ratio) ^ ( type == :crop ) # Image is too wide, the caret is the XOR operator
|
60
|
+
new_width, new_height = [ (new_height * aspect_ratio), new_height]
|
61
|
+
else #Image is too narrow
|
62
|
+
new_width, new_height = [ new_width, (new_width / aspect_ratio)]
|
63
|
+
end
|
64
|
+
|
65
|
+
[new_width, new_height].collect! { |v| v.round }
|
66
|
+
end
|
67
|
+
|
68
|
+
def extract_dimensions_for_crop(width, height, new_geometry)
|
69
|
+
extract_dimensions(width, height, new_geometry, :crop)
|
70
|
+
end
|
71
|
+
|
72
|
+
def extract_placement_for_crop(width, height, new_geometry)
|
73
|
+
new_width, new_height = convert_geometry(new_geometry)
|
74
|
+
x_offset = (width / 2.0) - (new_width / 2.0)
|
75
|
+
y_offset = (height / 2.0) - (new_height / 2.0)
|
76
|
+
[x_offset, y_offset].collect! { |v| v.round }
|
77
|
+
end
|
78
|
+
|
79
|
+
def convert_geometry(geometry)
|
80
|
+
geometry.split('x').map{|i| i.to_f }
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module UploadColumn
|
2
|
+
|
3
|
+
UploadError = Class.new(StandardError) unless defined?(UploadError)
|
4
|
+
ManipulationError = Class.new(UploadError) unless defined?(ManipulationError)
|
5
|
+
|
6
|
+
module Manipulators
|
7
|
+
|
8
|
+
module RMagick
|
9
|
+
|
10
|
+
def load_manipulator_dependencies #:nodoc:
|
11
|
+
require 'RMagick'
|
12
|
+
end
|
13
|
+
|
14
|
+
def process!(instruction = nil, &block)
|
15
|
+
if instruction.is_a?(Proc)
|
16
|
+
manipulate!(&instruction)
|
17
|
+
elsif instruction.to_s =~ /^c(\d+x\d+)$/
|
18
|
+
crop_resized!($1)
|
19
|
+
elsif instruction.to_s =~ /^(\d+x\d+)$/
|
20
|
+
resize!($1)
|
21
|
+
end
|
22
|
+
manipulate!(&block) if block
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convert the image to format
|
26
|
+
def convert!(format)
|
27
|
+
manipulate! do |img|
|
28
|
+
img.format = format.to_s.upcase
|
29
|
+
img
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Resize the image so that it will not exceed the dimensions passed
|
34
|
+
# via geometry, geometry should be a string, formatted like '200x100' where
|
35
|
+
# the first number is the height and the second is the width
|
36
|
+
def resize!( geometry )
|
37
|
+
manipulate! do |img|
|
38
|
+
img.change_geometry( geometry ) do |c, r, i|
|
39
|
+
i.resize(c,r)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Resize and crop the image so that it will have the exact dimensions passed
|
45
|
+
# via geometry, geometry should be a string, formatted like '200x100' where
|
46
|
+
# the first number is the height and the second is the width
|
47
|
+
def crop_resized!( geometry )
|
48
|
+
manipulate! do |img|
|
49
|
+
h, w = geometry.split('x')
|
50
|
+
img.crop_resized(h.to_i,w.to_i)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def manipulate!
|
55
|
+
image = ::Magick::Image.read(self.path)
|
56
|
+
|
57
|
+
if image.size > 1
|
58
|
+
list = ::Magick::ImageList.new
|
59
|
+
image.each do |frame|
|
60
|
+
list << yield( frame )
|
61
|
+
end
|
62
|
+
list.write(self.path)
|
63
|
+
else
|
64
|
+
yield( image.first ).write(self.path)
|
65
|
+
end
|
66
|
+
rescue ::Magick::ImageMagickError => e
|
67
|
+
# this is a more meaningful error message, which we could catch later
|
68
|
+
raise ManipulationError.new("Failed to manipulate with rmagick, maybe it is not an image? Original Error: #{e}")
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module UploadColumn::ActionControllerExtension
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.send :alias_method_chain, :url_for, :uploaded_file_check
|
5
|
+
base.helper_method :url_for_path
|
6
|
+
end
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
def url_for_with_uploaded_file_check(options = {}, *parameters_for_method_reference)
|
11
|
+
if(options.respond_to?(:public_path))
|
12
|
+
options.public_path
|
13
|
+
else
|
14
|
+
url_for_without_uploaded_file_check(options || {}, *parameters_for_method_reference)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def url_for_path(path)
|
19
|
+
request.protocol + request.host_with_port + path
|
20
|
+
end
|
21
|
+
|
22
|
+
# You can use +render_image+ in your controllers to render an image
|
23
|
+
# def picture
|
24
|
+
# @user = User.find(params[:id])
|
25
|
+
# render_image @user.picture
|
26
|
+
# end
|
27
|
+
# This of course, is not very useful at all (you could simply have linked to the image itself),
|
28
|
+
# However it is even possible to pass a block to render_image that allows manipulation using
|
29
|
+
# RMagick, here the fun begins:
|
30
|
+
# def solarize_picture
|
31
|
+
# @user = User.find(params[:id])
|
32
|
+
# render_image @user.picture do |img|
|
33
|
+
# img = img.segment
|
34
|
+
# img.solarize
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
# Note that like in UploadColumn::BaseUploadedFile.process you will need to 'carry' the image
|
38
|
+
# since most Rmagick methods do not modify the image itself but rather return the result of the
|
39
|
+
# transformation.
|
40
|
+
#
|
41
|
+
# Instead of passing an upload_column object to +render_image+ you can even pass a path String,
|
42
|
+
# if you do you will have to pass a :mime-type option as well though.
|
43
|
+
def render_image( file, options = {} )
|
44
|
+
format = if options.is_a?(Hash) then options[:force_format] else nil end
|
45
|
+
mime_type = if options.is_a?(String) then options else options[:mime_type] end
|
46
|
+
mime_type ||= file.mime_type
|
47
|
+
path = if file.is_a?( String ) then file else file.path end
|
48
|
+
headers["Content-Type"] = mime_type unless format
|
49
|
+
|
50
|
+
if block_given? or format
|
51
|
+
img = ::Magick::Image::read(path).first
|
52
|
+
img = yield( img ) if block_given?
|
53
|
+
img.format = format.to_s.upcase if format
|
54
|
+
render :text => img.to_blob, :layout => false
|
55
|
+
else
|
56
|
+
send_file( path )
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
ActionController::Base.send(:include, UploadColumn::ActionControllerExtension)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module UploadColumn::AssetTagExtension
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.send :alias_method_chain, :image_tag, :uploaded_file_check
|
5
|
+
end
|
6
|
+
|
7
|
+
def image_tag_with_uploaded_file_check(source, options = {})
|
8
|
+
if(source.respond_to?(:public_path))
|
9
|
+
image_tag_without_uploaded_file_check(source.public_path, options)
|
10
|
+
else
|
11
|
+
image_tag_without_uploaded_file_check(source, options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
ActionView::Helpers::AssetTagHelper.send(:include, UploadColumn::AssetTagExtension)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module UploadColumn::UploadColumnHelper
|
2
|
+
|
3
|
+
# Returns an input tag of the "file" type tailored for accessing an upload_column field
|
4
|
+
# (identified by method) on an object assigned to the template (identified by object).
|
5
|
+
# Additional options on the input tag can be passed as a hash with options.
|
6
|
+
#
|
7
|
+
# Example (call, result)
|
8
|
+
# upload_column_field( :user, :picture )
|
9
|
+
# <input id="user_picture_temp" name="user[picture_temp]" type="hidden" />
|
10
|
+
# <input id="user_picture" name="user[picture]" size="30" type="file" />
|
11
|
+
#
|
12
|
+
# Note: if you use file_field instead of upload_column_field, the file will not be
|
13
|
+
# stored across form redisplays.
|
14
|
+
def upload_column_field(object, method, options={})
|
15
|
+
file_field(object, method, options) + hidden_field(object, method.to_s + '_temp')
|
16
|
+
end
|
17
|
+
|
18
|
+
# A helper method for creating a form tag to use with uploadng files,
|
19
|
+
# it works exactly like Rails' form_tag, except that :multipart is always true
|
20
|
+
def upload_form_tag(url_for_options = {}, options = {}, *parameters_for_url, &proc)
|
21
|
+
options[:multipart] = true
|
22
|
+
form_tag( url_for_options, options, *parameters_for_url, &proc )
|
23
|
+
end
|
24
|
+
|
25
|
+
# A helper method for creating a form tag to use with uploadng files,
|
26
|
+
# it works exactly like Rails' form_for, except that :multipart is always true
|
27
|
+
def upload_form_for(*args, &block)
|
28
|
+
options = args.extract_options!
|
29
|
+
options[:html] ||= {}
|
30
|
+
options[:html][:multipart] = true
|
31
|
+
args.push(options)
|
32
|
+
|
33
|
+
form_for(*args, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class ActionView::Helpers::FormBuilder #:nodoc:
|
39
|
+
self.field_helpers += ['upload_column_field']
|
40
|
+
def upload_column_field(method, options = {})
|
41
|
+
@template.send(:upload_column_field, @object_name, method, options.merge(:object => @object))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
ActionView::Base.send(:include, UploadColumn::UploadColumnHelper)
|
@@ -0,0 +1,176 @@
|
|
1
|
+
begin; require 'mime/types'; rescue Exception; end
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module UploadColumn
|
6
|
+
# Sanitize is a base class that takes care of all the dirtywork when dealing with file uploads.
|
7
|
+
# it is subclassed as UploadedFile in UploadColumn, which does most of the upload magic, but if
|
8
|
+
# you want to roll you own uploading system, SanitizedFile might be for you since it takes care
|
9
|
+
# of a lot of the unfun stuff.
|
10
|
+
#
|
11
|
+
# Usage is pretty simple, just do SanitizedFile.new(some_uploaded_file) and you're good to go
|
12
|
+
# you can now use #copy_to and #move_to to place the file wherever you want, whether it is a StringIO
|
13
|
+
# or a TempFile.
|
14
|
+
#
|
15
|
+
# SanitizedFile also deals with content type detection, which it does either through the 'file' *nix exec
|
16
|
+
# or (if you are stuck on Windows) through the MIME::Types library (not to be confused with Rails' Mime class!).
|
17
|
+
class SanitizedFile
|
18
|
+
|
19
|
+
attr_reader :basename, :extension
|
20
|
+
|
21
|
+
def initialize(file, options = {})
|
22
|
+
@options = options
|
23
|
+
if file && file.instance_of?(String) && !file.empty?
|
24
|
+
@path = file
|
25
|
+
self.filename = File.basename(file)
|
26
|
+
else
|
27
|
+
@file = file
|
28
|
+
self.filename = self.original_filename unless self.empty?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the filename before sanitation took place
|
33
|
+
def original_filename
|
34
|
+
@original_filename ||= if @file and @file.respond_to?(:original_filename)
|
35
|
+
@file.original_filename
|
36
|
+
elsif self.path
|
37
|
+
File.basename(self.path)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the files properly sanitized filename.
|
42
|
+
def filename
|
43
|
+
@filename ||= (self.extension && !self.extension.empty?) ? "#{self.basename}.#{self.extension}" : self.basename
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the file's size
|
47
|
+
def size
|
48
|
+
return @file.size if @file.respond_to?(:size)
|
49
|
+
File.size(self.path) rescue nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the full path to the file
|
53
|
+
def path
|
54
|
+
@path ||= File.expand_path(@file.path) rescue nil
|
55
|
+
end
|
56
|
+
|
57
|
+
# Checks if the file is empty.
|
58
|
+
def empty?
|
59
|
+
(@file.nil? && @path.nil?) || self.size.nil? || self.size.zero?
|
60
|
+
end
|
61
|
+
|
62
|
+
# Checks if the file exists
|
63
|
+
def exists?
|
64
|
+
File.exists?(self.path) if self.path
|
65
|
+
end
|
66
|
+
|
67
|
+
# Moves the file to 'path'
|
68
|
+
def move_to(path)
|
69
|
+
if copy_file(path)
|
70
|
+
# FIXME: This gets pretty broken in UploadedFile. E.g. moving avatar-thumb.jpg will change the filename
|
71
|
+
# to avatar-thumb-thumb.jpg
|
72
|
+
@basename, @extension = split_extension(File.basename(path))
|
73
|
+
@file = nil
|
74
|
+
@filename = nil
|
75
|
+
@path = path
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Copies the file to 'path' and returns a new SanitizedFile that points to the copy.
|
80
|
+
def copy_to(path)
|
81
|
+
copy = self.clone
|
82
|
+
copy.move_to(path)
|
83
|
+
return copy
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the content_type of the file as determined through the MIME::Types library or through a *nix exec.
|
87
|
+
def content_type
|
88
|
+
unless content_type = get_content_type_from_exec || get_content_type_from_mime_types
|
89
|
+
content_type ||= @file.content_type.chomp if @file.respond_to?(:content_type) and @file.content_type
|
90
|
+
end
|
91
|
+
return content_type
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def copy_file(path)
|
97
|
+
unless self.empty?
|
98
|
+
# create the directory if it doesn't exist
|
99
|
+
FileUtils.mkdir_p(File.dirname(path)) unless File.exists?(File.dirname(path))
|
100
|
+
# stringios don't have a path and can't be copied
|
101
|
+
if not self.path and @file.respond_to?(:read)
|
102
|
+
@file.rewind # Make sure we are at the beginning of the buffer
|
103
|
+
File.open(path, "wb") { |f| f.write(@file.read) }
|
104
|
+
else
|
105
|
+
begin
|
106
|
+
FileUtils.cp(self.path, path)
|
107
|
+
rescue ArgumentError
|
108
|
+
end
|
109
|
+
end
|
110
|
+
File.chmod(@options[:permissions], path) if @options[:permissions]
|
111
|
+
return true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def filename=(filename)
|
116
|
+
basename, extension = split_extension(filename)
|
117
|
+
@basename = sanitize(basename)
|
118
|
+
@extension = correct_file_extension(extension)
|
119
|
+
end
|
120
|
+
|
121
|
+
# tries to identify the mime-type of file and correct self's extension
|
122
|
+
# based on the found mime-type
|
123
|
+
def correct_file_extension(ext)
|
124
|
+
if @options[:fix_file_extensions] && defined?(MIME::Types)
|
125
|
+
if mimes = MIME::Types[self.content_type]
|
126
|
+
return mimes.first.extensions.first unless mimes.first.extensions.empty?
|
127
|
+
end
|
128
|
+
end
|
129
|
+
return ext.downcase
|
130
|
+
end
|
131
|
+
|
132
|
+
# Try to use *nix exec to fetch content type
|
133
|
+
def get_content_type_from_exec
|
134
|
+
if @options[:get_content_type_from_file_exec] and not self.path.empty?
|
135
|
+
return system_call(%(file -bi "#{self.path}")).chomp.scan(/^[a-z0-9\-_]+\/[a-z0-9\-_]+/).first
|
136
|
+
end
|
137
|
+
rescue
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
|
141
|
+
def system_call(command)
|
142
|
+
`#{command}`
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_content_type_from_mime_types
|
146
|
+
if @extension and defined?(MIME::Types)
|
147
|
+
mimes = MIME::Types.of(@extension)
|
148
|
+
return mimes.first.content_type rescue nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def sanitize(name)
|
153
|
+
# Sanitize the filename, to prevent hacking
|
154
|
+
name = File.basename(name.gsub("\\", "/")) # work-around for IE
|
155
|
+
name.gsub!(/[^a-zA-Z0-9\.\-\+_]/,"_")
|
156
|
+
name = "_#{name}" if name =~ /^\.+$/
|
157
|
+
name = "unnamed" if name.size == 0
|
158
|
+
return name.downcase
|
159
|
+
end
|
160
|
+
|
161
|
+
def split_extension(fn)
|
162
|
+
# regular expressions to try for identifying extensions
|
163
|
+
ext_regexps = [
|
164
|
+
/^(.+)\.([^\.]{1,3}\.[^\.]{1,4})$/, # matches "something.tar.gz"
|
165
|
+
/^(.+)\.([^\.]+)$/ # matches "something.jpg"
|
166
|
+
]
|
167
|
+
ext_regexps.each do |regexp|
|
168
|
+
if fn =~ regexp
|
169
|
+
return $1, $2
|
170
|
+
end
|
171
|
+
end
|
172
|
+
return fn, "" # In case we weren't able to split the extension
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|