attachment_saver 1.0.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/MIT-LICENSE +19 -0
- data/README +137 -0
- data/Rakefile +16 -0
- data/attachment_saver.gemspec +41 -0
- data/init.rb +1 -0
- data/lib/attachment_saver.rb +171 -0
- data/lib/attachment_saver/version.rb +3 -0
- data/lib/attachment_saver_errors.rb +3 -0
- data/lib/datastores/file_system.rb +189 -0
- data/lib/datastores/in_column.rb +49 -0
- data/lib/misc/extended_tempfile.rb +12 -0
- data/lib/misc/file_size.rb +5 -0
- data/lib/misc/image_science_extensions.rb +102 -0
- data/lib/misc/mini_magick_extensions.rb +89 -0
- data/lib/processors/image.rb +187 -0
- data/lib/processors/image_science.rb +94 -0
- data/lib/processors/mini_magick.rb +103 -0
- data/lib/processors/r_magick.rb +120 -0
- data/test/attachment_saver_test.rb +162 -0
- data/test/database.yml +3 -0
- data/test/file_system_datastore_test.rb +468 -0
- data/test/fixtures/broken.jpg +1 -0
- data/test/fixtures/emptyextension. +0 -0
- data/test/fixtures/noextension +0 -0
- data/test/fixtures/test.jpg +0 -0
- data/test/fixtures/test.js +1 -0
- data/test/fixtures/wrongextension.png +0 -0
- data/test/image_fixtures.rb +69 -0
- data/test/image_operations.rb +114 -0
- data/test/image_processor_test.rb +67 -0
- data/test/image_processor_test_common.rb +81 -0
- data/test/image_science_processor_test.rb +20 -0
- data/test/in_column_datastore_test.rb +115 -0
- data/test/mini_magick_processor_test.rb +20 -0
- data/test/model_test.rb +205 -0
- data/test/public/.empty +0 -0
- data/test/rmagick_processor_test.rb +20 -0
- data/test/schema.rb +41 -0
- data/test/test_helper.rb +49 -0
- metadata +223 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'image_science'
|
2
|
+
require 'misc/image_science_extensions'
|
3
|
+
require 'misc/extended_tempfile'
|
4
|
+
require 'processors/image'
|
5
|
+
|
6
|
+
class ImageScienceProcessorError < ImageProcessorError; end
|
7
|
+
|
8
|
+
module AttachmentSaver
|
9
|
+
module Processors
|
10
|
+
module ImageScience
|
11
|
+
include Image
|
12
|
+
|
13
|
+
def with_image(filename, &block)
|
14
|
+
::ImageScience.with_image(filename) {|image| block.call(image.extend(Operations))}
|
15
|
+
end
|
16
|
+
|
17
|
+
def with_image_attributes(filename, &block)
|
18
|
+
return with_image(filename, &block) unless ::ImageScience.respond_to?(:with_image_attributes)
|
19
|
+
::ImageScience.with_image_attributes(filename) {|image| block.call(image)}
|
20
|
+
end
|
21
|
+
|
22
|
+
def examine_attachment
|
23
|
+
with_image_attributes(uploaded_file_path) do |original_image|
|
24
|
+
self.width = original_image.width if respond_to?(:width)
|
25
|
+
self.height = original_image.height if respond_to?(:height)
|
26
|
+
self.content_type = original_image.mime_type unless self.class.attachment_options[:keep_content_type] || original_image.mime_type.nil?
|
27
|
+
self.file_extension = original_image.file_type_extension unless self.class.attachment_options[:keep_file_extension] || original_image.file_type_extension.nil?
|
28
|
+
end
|
29
|
+
rescue AttachmentSaverError
|
30
|
+
raise
|
31
|
+
rescue Exception => ex
|
32
|
+
raise ImageScienceProcessorError, "#{ex.class}: #{ex.message}", ex.backtrace
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_image(original_image, derived_format_name, resize_format)
|
36
|
+
resize_format = Image.from_geometry_string(resize_format) if resize_format.is_a?(String)
|
37
|
+
|
38
|
+
original_image.send(*resize_format) do |derived_image|
|
39
|
+
return nil unless want_format?(derived_format_name, derived_image.width, derived_image.height)
|
40
|
+
|
41
|
+
if derived_image.file_type == 'GIF' # && derived_image.depth != 8 # TODO: submit patch to add depth attribute
|
42
|
+
# as a special case hack, don't try and save 24-bit derived images into 8-bit-only GIF format
|
43
|
+
# (ImageScience doesn't resample back down, so it throws errors if we try to do that)
|
44
|
+
derived_content_type = 'image/png'
|
45
|
+
derived_extension = 'png'
|
46
|
+
else
|
47
|
+
# both original_filename and content_type must be defined for parents when using image processing
|
48
|
+
# - but apps can just define them using attr_accessor if they don't want them persisted to db
|
49
|
+
derived_content_type = derived_image.mime_type || original_image.mime_type || content_type # note that mime_type will return nil instead of returning any of the freeimage-invented content types
|
50
|
+
derived_extension = (derived_image.file_type || file_extension).downcase # in fact, derived_image.file_type should always work; the only situation in which it could return nil is if freeimage is extended to support a new image format but image_science_extensions isn't updated
|
51
|
+
end
|
52
|
+
|
53
|
+
# we leverage tempfiles as discussed in the uploaded_file method
|
54
|
+
temp = ExtendedTempfile.new("asitemp", tempfile_directory, derived_extension)
|
55
|
+
temp.binmode
|
56
|
+
temp.close
|
57
|
+
derived_image.save(temp.path)
|
58
|
+
temp.open # we close & reopen so we see the file the processor wrote to, even if it created a new file rather than writing into our tempfile
|
59
|
+
|
60
|
+
{ :format_name => derived_format_name.to_s,
|
61
|
+
:width => derived_image.width,
|
62
|
+
:height => derived_image.height,
|
63
|
+
:content_type => derived_content_type,
|
64
|
+
:file_extension => derived_extension,
|
65
|
+
:uploaded_data => temp }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module Operations
|
70
|
+
include AttachmentSaver::Processors::Image::Operations
|
71
|
+
|
72
|
+
def file_type_extension
|
73
|
+
file_type.downcase
|
74
|
+
end
|
75
|
+
|
76
|
+
def resize_to(new_width, new_height, &block)
|
77
|
+
resize(new_width, new_height) do |image|
|
78
|
+
image.extend Operations
|
79
|
+
block.call(image) # ImageScience itself doesn't accept a block argument (it yields only)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def crop_to(new_width, new_height, &block) # crops to the center
|
84
|
+
left = (width - new_width)/2
|
85
|
+
right = (height - new_height)/2
|
86
|
+
with_crop(left, right, left + new_width, right + new_height) do |image|
|
87
|
+
image.extend Operations
|
88
|
+
block.call(image) # as for resize, with_crop doesn't take a block itself
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'mini_magick'
|
2
|
+
require 'misc/mini_magick_extensions'
|
3
|
+
require 'misc/extended_tempfile'
|
4
|
+
require 'processors/image'
|
5
|
+
|
6
|
+
class MiniMagickProcessorError < ImageProcessorError; end
|
7
|
+
|
8
|
+
module AttachmentSaver
|
9
|
+
module Processors
|
10
|
+
module MiniMagick
|
11
|
+
include Image
|
12
|
+
|
13
|
+
def with_image(filename, &block)
|
14
|
+
# note that we are instantiating minimagick on the file itself, not a copy (which is
|
15
|
+
# what gets produced if you call from_file); we don't do any mutating operations on
|
16
|
+
# our instances themselves (resize_to and crop_to create new instances).
|
17
|
+
if ::MiniMagick::Image.respond_to?(:open) # v3
|
18
|
+
image = ::MiniMagick::Image.open(filename)
|
19
|
+
else # v1
|
20
|
+
image = ::MiniMagick::Image.new(filename)
|
21
|
+
end
|
22
|
+
block.call(image.extend(Operations))
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_image_attributes(filename, &block)
|
26
|
+
# MiniMagick doesn't actually load the image, it just keeps a reference to the filename
|
27
|
+
# and invokes the imagemagick programs to determine attributes
|
28
|
+
with_image(filename, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def examine_attachment
|
32
|
+
with_image_attributes(uploaded_file_path) do |original_image|
|
33
|
+
self.content_type = original_image.mime_type unless self.class.attachment_options[:keep_content_type] || original_image.mime_type.blank?
|
34
|
+
self.file_extension = original_image.file_type_extension unless self.class.attachment_options[:keep_file_extension] || original_image.file_type_extension.blank?
|
35
|
+
self.width = original_image.width if respond_to?(:width)
|
36
|
+
self.height = original_image.height if respond_to?(:height)
|
37
|
+
end
|
38
|
+
rescue AttachmentSaverError
|
39
|
+
raise
|
40
|
+
rescue Exception => ex
|
41
|
+
raise MiniMagickProcessorError, "#{ex.class}: #{ex.message}", ex.backtrace
|
42
|
+
end
|
43
|
+
|
44
|
+
def process_image(original_image, derived_format_name, resize_format)
|
45
|
+
resize_format = Image.from_geometry_string(resize_format) if resize_format.is_a?(String)
|
46
|
+
|
47
|
+
original_image.send(*resize_format) do |derived_image|
|
48
|
+
return nil unless want_format?(derived_format_name, derived_image.width, derived_image.height)
|
49
|
+
|
50
|
+
# both original_filename and content_type must be defined for parents when using image processing
|
51
|
+
# - but apps can just define them using attr_accessor if they don't want them persisted to db
|
52
|
+
derived_content_type = derived_image.mime_type || original_image.mime_type || content_type
|
53
|
+
derived_extension = derived_image.file_type_extension
|
54
|
+
|
55
|
+
# we leverage tempfiles as discussed in the uploaded_file method
|
56
|
+
temp = ExtendedTempfile.new("asmtemp", tempfile_directory, derived_extension)
|
57
|
+
temp.binmode
|
58
|
+
temp.close
|
59
|
+
derived_image.write(temp.path)
|
60
|
+
temp.open # we close & reopen so we see the file the processor wrote to, even if it created a new file rather than writing into our tempfile
|
61
|
+
|
62
|
+
{ :format_name => derived_format_name.to_s,
|
63
|
+
:width => derived_image.width,
|
64
|
+
:height => derived_image.height,
|
65
|
+
:content_type => derived_content_type,
|
66
|
+
:file_extension => derived_extension,
|
67
|
+
:uploaded_data => temp }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module Operations
|
72
|
+
include AttachmentSaver::Processors::Image::Operations
|
73
|
+
|
74
|
+
def file_type_extension
|
75
|
+
case format.downcase
|
76
|
+
when 'jpeg' then 'jpg'
|
77
|
+
else format.downcase
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def format; @__format ||= self[:format]; end # cached as each call to [] results in a process execution!
|
82
|
+
def width; @__width ||= self[:width]; end # note that we can cache as we don't ever modify instances -
|
83
|
+
def height; @__height ||= self[:height]; end # whereas MiniMagick may, in general.
|
84
|
+
|
85
|
+
def resize_to(new_width, new_height, &block)
|
86
|
+
image = dup
|
87
|
+
image.resize("#{new_width}x#{new_height}!")
|
88
|
+
image.extend Operations
|
89
|
+
block.call(image)
|
90
|
+
end
|
91
|
+
|
92
|
+
def crop_to(new_width, new_height, &block) # crops to the center
|
93
|
+
left = (width - new_width)/2
|
94
|
+
right = (height - new_height)/2
|
95
|
+
image = dup
|
96
|
+
image << "-crop #{new_width}x#{new_height}+#{left}+#{right} +repage" # mini_magick's #crop doesn't support the repage flag
|
97
|
+
image.extend Operations
|
98
|
+
block.call(image)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
begin
|
2
|
+
require 'RMagick'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rmagick'
|
5
|
+
end
|
6
|
+
require 'misc/extended_tempfile'
|
7
|
+
require 'processors/image'
|
8
|
+
|
9
|
+
class RMagickProcessorError < ImageProcessorError; end
|
10
|
+
|
11
|
+
module AttachmentSaver
|
12
|
+
module Processors
|
13
|
+
module RMagick
|
14
|
+
include Image
|
15
|
+
|
16
|
+
def with_image(filename, &block)
|
17
|
+
image = Magick::Image.read(filename).first
|
18
|
+
block.call(image.extend(Operations))
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_image_attributes(filename, &block)
|
22
|
+
image = Magick::Image.ping(filename).first
|
23
|
+
block.call(image.extend(Operations))
|
24
|
+
end
|
25
|
+
|
26
|
+
def examine_attachment
|
27
|
+
with_image_attributes(uploaded_file_path) do |original_image|
|
28
|
+
self.width = original_image.width if respond_to?(:width)
|
29
|
+
self.height = original_image.height if respond_to?(:height)
|
30
|
+
self.content_type = original_image.corrected_mime_type unless self.class.attachment_options[:keep_content_type] || original_image.corrected_mime_type.nil?
|
31
|
+
self.file_extension = original_image.file_type_extension unless self.class.attachment_options[:keep_file_extension] || original_image.file_type_extension.nil?
|
32
|
+
end
|
33
|
+
rescue AttachmentSaverError
|
34
|
+
raise
|
35
|
+
rescue Exception => ex
|
36
|
+
raise RMagickProcessorError, "#{ex.class}: #{ex.message}", ex.backtrace
|
37
|
+
end
|
38
|
+
|
39
|
+
def process_image(original_image, derived_format_name, resize_format)
|
40
|
+
resize_format = Image.from_geometry_string(resize_format) if resize_format.is_a?(String)
|
41
|
+
|
42
|
+
result = original_image.send(*resize_format) do |derived_image|
|
43
|
+
return nil unless want_format?(derived_format_name, derived_image.width, derived_image.height)
|
44
|
+
|
45
|
+
# both original_filename and content_type must be defined for parents when using image processing
|
46
|
+
# - but apps can just define them using attr_accessor if they don't want them persisted to db
|
47
|
+
derived_content_type = derived_image.corrected_mime_type || original_image.corrected_mime_type || content_type
|
48
|
+
derived_extension = derived_image.file_type_extension
|
49
|
+
|
50
|
+
# we leverage tempfiles as discussed in the uploaded_file method
|
51
|
+
temp = ExtendedTempfile.new("asrtemp", tempfile_directory, derived_extension)
|
52
|
+
temp.binmode
|
53
|
+
temp.close
|
54
|
+
derived_image.write(temp.path)
|
55
|
+
temp.open # we close & reopen so we see the file the processor wrote to, even if it created a new file rather than writing into our tempfile
|
56
|
+
|
57
|
+
{ :format_name => derived_format_name.to_s,
|
58
|
+
:width => derived_image.width,
|
59
|
+
:height => derived_image.height,
|
60
|
+
:content_type => derived_content_type,
|
61
|
+
:file_extension => derived_extension,
|
62
|
+
:uploaded_data => temp }
|
63
|
+
end
|
64
|
+
|
65
|
+
# modern versions of RMagick don't leak memory. however, the (many and large) internal
|
66
|
+
# buffers malloced inside the ImageMagick library are not allocated via the Ruby memory
|
67
|
+
# management functions. as Ruby GC runs are normally triggered at the point when those Ruby
|
68
|
+
# memory management functions request a larger heap, ImageMagick's extra allocations will
|
69
|
+
# not trigger a GC run. so while no memory has been leaked - all the allocations by the
|
70
|
+
# ImageMagick library *will* get freed when GC runs - GC will typically not run even if you
|
71
|
+
# process a series of images and end up using all of the memory that can be made available
|
72
|
+
# to the process, at which point your process dies! until such time as RMagick rewraps the
|
73
|
+
# ImageMagick memory allocation functions to put them through Ruby's (as was done in the
|
74
|
+
# as-yet-uncompleted MagickWand project), we force a GC after each image processing to
|
75
|
+
# ensure that your processes stay happy.
|
76
|
+
GC.start
|
77
|
+
result
|
78
|
+
end
|
79
|
+
|
80
|
+
module Operations
|
81
|
+
include AttachmentSaver::Processors::Image::Operations
|
82
|
+
|
83
|
+
def corrected_mime_type
|
84
|
+
case mime_type
|
85
|
+
when 'image/x-jpeg' then 'image/jpeg'
|
86
|
+
when 'image/x-magick' then nil
|
87
|
+
else mime_type
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def file_type_extension
|
92
|
+
case format.downcase
|
93
|
+
when 'jpeg' then 'jpg'
|
94
|
+
else format.downcase
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def width
|
99
|
+
columns
|
100
|
+
end
|
101
|
+
|
102
|
+
def height
|
103
|
+
rows
|
104
|
+
end
|
105
|
+
|
106
|
+
def resize_to(new_width, new_height, &block)
|
107
|
+
image = resize(new_width, new_height)
|
108
|
+
image.extend Operations
|
109
|
+
block.call(image)
|
110
|
+
end
|
111
|
+
|
112
|
+
def crop_to(new_width, new_height, &block) # crops to the center
|
113
|
+
image = crop(Magick::CenterGravity, new_width, new_height, true)
|
114
|
+
image.extend Operations
|
115
|
+
block.call(image)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
require 'attachment_saver'
|
3
|
+
|
4
|
+
class AttachmentSaverTest < Test::Unit::TestCase
|
5
|
+
def test_split_filename
|
6
|
+
assert_equal ['a', nil], AttachmentSaver::split_filename('a')
|
7
|
+
assert_equal ['a', ''], AttachmentSaver::split_filename('a.')
|
8
|
+
assert_equal ['a', 'b'], AttachmentSaver::split_filename('a.b')
|
9
|
+
assert_equal ['a', 'bcde'], AttachmentSaver::split_filename('a.bcde')
|
10
|
+
assert_equal ['a.bcde', 'fgh'], AttachmentSaver::split_filename('a.bcde.fgh')
|
11
|
+
end
|
12
|
+
|
13
|
+
class SomeModel
|
14
|
+
include AttachmentSaver::InstanceMethods
|
15
|
+
class_attribute :attachment_options
|
16
|
+
|
17
|
+
attr_accessor :size, :content_type, :original_filename
|
18
|
+
end
|
19
|
+
|
20
|
+
module TempfileAttributes
|
21
|
+
def original_filename
|
22
|
+
"test.txt"
|
23
|
+
end
|
24
|
+
|
25
|
+
def content_type
|
26
|
+
"text/plain"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module ExtensionlessAttributes
|
31
|
+
def original_filename
|
32
|
+
"test"
|
33
|
+
end
|
34
|
+
|
35
|
+
def content_type
|
36
|
+
"text/plain"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module OriginalFilenameHasPathAttributes
|
41
|
+
def original_filename
|
42
|
+
" c:\\test/foo.txt "
|
43
|
+
end
|
44
|
+
|
45
|
+
def content_type
|
46
|
+
"text/plain"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def contents_of(file)
|
51
|
+
file.rewind
|
52
|
+
file.read
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_default_methods
|
56
|
+
model = SomeModel.new
|
57
|
+
assert_equal nil, model.uploaded_data
|
58
|
+
assert_equal nil, model.uploaded_file
|
59
|
+
assert_equal false, model.process_attachment?
|
60
|
+
assert File.directory?(model.tempfile_directory)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_uploaded_data_setters_and_extensions
|
64
|
+
SomeModel.attachment_options = {}
|
65
|
+
|
66
|
+
model = SomeModel.new
|
67
|
+
model.uploaded_data = 'test #1'
|
68
|
+
assert_equal 7, model.size
|
69
|
+
assert_equal nil, model.content_type
|
70
|
+
assert_equal nil, model.original_filename
|
71
|
+
assert_equal 'test #1', model.uploaded_data # before converting to an uploaded_file
|
72
|
+
assert_not_equal nil, model.uploaded_file
|
73
|
+
assert model.uploaded_file.is_a?(Tempfile)
|
74
|
+
assert_equal model.uploaded_file.object_id, model.uploaded_file.object_id, 'uploaded_file should return the same instance each time'
|
75
|
+
assert_equal 'test #1', model.uploaded_data # after converting to an uploaded_file
|
76
|
+
assert_equal 'test #1', contents_of(model.uploaded_file)
|
77
|
+
assert_equal 'test #1', model.uploaded_data
|
78
|
+
assert_equal 'bin', model.file_extension
|
79
|
+
model.file_extension = 'ext'
|
80
|
+
assert_equal 'ext', model.file_extension
|
81
|
+
|
82
|
+
model = SomeModel.new
|
83
|
+
model.uploaded_data = StringIO.new('test #2')
|
84
|
+
assert_equal 7, model.size
|
85
|
+
assert_equal nil, model.content_type
|
86
|
+
assert_equal nil, model.original_filename
|
87
|
+
assert_not_equal nil, model.uploaded_file
|
88
|
+
assert model.uploaded_file.is_a?(Tempfile)
|
89
|
+
assert_equal model.uploaded_file.object_id, model.uploaded_file.object_id, 'uploaded_file should return the same instance each time'
|
90
|
+
assert_equal 'test #2', model.uploaded_data
|
91
|
+
assert_equal 'test #2', contents_of(model.uploaded_file)
|
92
|
+
assert_equal 'test #2', model.uploaded_data
|
93
|
+
assert_equal 'bin', model.file_extension
|
94
|
+
model.file_extension = 'ext'
|
95
|
+
assert_equal 'ext', model.file_extension
|
96
|
+
|
97
|
+
Tempfile.open('test') do |tempfile|
|
98
|
+
tempfile.write('test #3')
|
99
|
+
model = SomeModel.new
|
100
|
+
model.uploaded_data = tempfile
|
101
|
+
assert_equal 7, model.size
|
102
|
+
assert_equal nil, model.content_type
|
103
|
+
assert_equal nil, model.original_filename
|
104
|
+
assert_equal tempfile.object_id, model.uploaded_file.object_id, 'uploaded_file should return the originally given tempfile'
|
105
|
+
assert_equal 'test #3', model.uploaded_data
|
106
|
+
assert_equal 'test #3', contents_of(model.uploaded_file)
|
107
|
+
assert_equal 'test #3', model.uploaded_data
|
108
|
+
assert_equal 'bin', model.file_extension
|
109
|
+
model.file_extension = 'ext'
|
110
|
+
assert_equal 'ext', model.file_extension
|
111
|
+
end
|
112
|
+
|
113
|
+
Tempfile.open('test') do |tempfile|
|
114
|
+
tempfile.extend TempfileAttributes
|
115
|
+
tempfile.write('test #4')
|
116
|
+
model = SomeModel.new
|
117
|
+
model.uploaded_data = tempfile
|
118
|
+
assert_equal 7, model.size
|
119
|
+
assert_equal "text/plain", model.content_type
|
120
|
+
assert_equal "test.txt", model.original_filename
|
121
|
+
assert_equal tempfile.object_id, model.uploaded_file.object_id, 'uploaded_file should return the originally given tempfile'
|
122
|
+
assert_equal 'test #4', model.uploaded_data
|
123
|
+
assert_equal 'test #4', contents_of(model.uploaded_file)
|
124
|
+
assert_equal 'test #4', model.uploaded_data
|
125
|
+
assert_equal 'txt', model.file_extension
|
126
|
+
model.file_extension = 'ext'
|
127
|
+
assert_equal 'ext', model.file_extension
|
128
|
+
end
|
129
|
+
|
130
|
+
Tempfile.open('test') do |tempfile|
|
131
|
+
tempfile.extend ExtensionlessAttributes
|
132
|
+
model = SomeModel.new
|
133
|
+
model.uploaded_data = tempfile
|
134
|
+
assert_equal "test", model.original_filename
|
135
|
+
assert_equal 'bin', model.file_extension
|
136
|
+
model.file_extension = 'ext'
|
137
|
+
assert_equal 'ext', model.file_extension
|
138
|
+
end
|
139
|
+
|
140
|
+
Tempfile.open('test') do |tempfile|
|
141
|
+
tempfile.extend OriginalFilenameHasPathAttributes
|
142
|
+
|
143
|
+
model = SomeModel.new
|
144
|
+
model.uploaded_data = tempfile
|
145
|
+
assert_equal "foo.txt", model.original_filename
|
146
|
+
assert_equal 'txt', model.file_extension
|
147
|
+
|
148
|
+
SomeModel.attachment_options = {:keep_original_filename_path => true}
|
149
|
+
model = SomeModel.new
|
150
|
+
model.uploaded_data = tempfile
|
151
|
+
assert_equal "c:\\test/foo.txt", model.original_filename
|
152
|
+
assert_equal 'txt', model.file_extension
|
153
|
+
end
|
154
|
+
|
155
|
+
model = SomeModel.new
|
156
|
+
model.uploaded_data = '' # this is what controllers get sent when there's a file field but no file selected; attachment_saver accordingly handles blank strings as a special case
|
157
|
+
assert_equal nil, model.uploaded_file
|
158
|
+
assert_equal nil, model.size
|
159
|
+
assert_equal nil, model.content_type
|
160
|
+
assert_equal nil, model.original_filename
|
161
|
+
end
|
162
|
+
end
|