crabgrass_media 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d09be8b214809ae6616a5126df305f329876c7ad
4
+ data.tar.gz: ce7bc5c445c29e015eccde25093b94d5d5c3a419
5
+ SHA512:
6
+ metadata.gz: d87316cc0f0719e2f2f7ab0cd222b13e2dad38c38962aa104bf433c957e5b9f1102011a6e2569000cb170f3199c16dc3a67b998a1e521b3f07f1868a8ade3b28
7
+ data.tar.gz: 5aa9cfe9a207f706eb78fcf88963c8340e30f8693acb4f99317286eeb0e8f6d1c81749e6d785f0af1de6380d1436a6b9cbf937d428cd5f1c82335361c809534c
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ Crabgrass::Media is the Media engine of Crabgrass.
2
+
3
+ Crabgrass is a web application designed for activist groups to be better able to collaborate online. Mostly, it is a glorified wiki with fine-grain control over access rights.
4
+
5
+ Crabgrass::Media is a rails engine to do media transformations.
6
+
7
+ You can add new media transformations by subclassing Transmogrifier.
8
+
9
+ Example usage:
10
+
11
+ ```ruby
12
+ transmog = Media.transmogrifier(:input_file => 'myfile.odt', :output_file => 'myfile.jpg')
13
+ status = transmog.run do |progress|
14
+ puts progress
15
+ end
16
+ ```
17
+
18
+ Requirements:
19
+
20
+
21
+ Strongly suggested:
22
+
23
+ ```sh
24
+ gem install mime-types
25
+ ```
26
+
27
+ Crabgrass and Crabgrass::Media are based on Ruby on Rails and MySQL.
28
+ They are released under the AGPL license, version 3.
29
+
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Media'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,9 @@
1
+ unless defined?(Media::TMP_PATH)
2
+ if defined?(Rails)
3
+ Media::TMP_PATH = File.join(Rails.root, 'tmp', 'media')
4
+ else
5
+ Media::TMP_PATH = File.join('', 'tmp', 'media')
6
+ end
7
+ end
8
+
9
+ FileUtils.mkdir_p(Media::TempFile.tempfile_path) unless File.exist?(Media::TempFile.tempfile_path)
data/lib/media.rb ADDED
@@ -0,0 +1,64 @@
1
+ require 'media/mime_type'
2
+ require 'media/temp_file'
3
+ require 'media/transmogrifier'
4
+ require 'media/transmogrifiers/graphicsmagick'
5
+ require 'media/transmogrifiers/inkscape'
6
+ require 'media/transmogrifiers/libremagick'
7
+ require 'media/transmogrifiers/libreoffice'
8
+
9
+ module Media
10
+
11
+ #
12
+ # creates a new instance of transmogrifier suitable for turning
13
+ # input into output.
14
+ #
15
+ def self.transmogrifier(options)
16
+ if options[:input_type]
17
+ input_type = Media::MimeType.simple options[:input_type]
18
+ elsif options[:input_file]
19
+ input_type = Media::MimeType.mime_type_from_extension options[:input_file]
20
+ else
21
+ raise ArgumentError.new
22
+ end
23
+
24
+ if options[:output_type]
25
+ output_type = Media::MimeType.simple options[:output_type]
26
+ elsif options[:output_file]
27
+ output_type = Media::MimeType.mime_type_from_extension options[:output_file]
28
+ else
29
+ raise ArgumentError.new
30
+ end
31
+
32
+ unless input_type and output_type
33
+ raise ArgumentError.new("Both input and output types are required (given %s -> %s)." % [input_type||'nil', output_type||'nil'])
34
+ end
35
+
36
+ transmog_class = Transmogrifier.find_class(input_type, output_type)
37
+ if transmog_class
38
+ transmog_class.new(options)
39
+ end
40
+ end
41
+
42
+ def self.may_produce?(src_type, dst_type)
43
+ Transmogrifier.find_class(src_type, dst_type)
44
+ end
45
+
46
+ #
47
+ # special graphicsmagick hooks.
48
+ # we use graphicsmagick in order to parse the dimensions of image files.
49
+ #
50
+
51
+ def self.has_dimensions?(mime_type)
52
+ if Transmogrifier.list["Media::GraphicsMagickTransmogrifier"]
53
+ Transmogrifier.list["Media::GraphicsMagickTransmogrifier"].input_types.include? mime_type
54
+ end
55
+ end
56
+
57
+ def self.dimensions(filepath)
58
+ if Transmogrifier.list["Media::GraphicsMagickTransmogrifier"]
59
+ Transmogrifier.list["Media::GraphicsMagickTransmogrifier"].dimensions filepath
60
+ end
61
+ end
62
+
63
+ end
64
+
@@ -0,0 +1,272 @@
1
+ begin
2
+ require 'mime/types'
3
+ rescue LoadError => exc
4
+ puts "WARNING! no mime-types gem installed"
5
+ puts "you should really do 'gem install mime-types'"
6
+ end
7
+
8
+
9
+ module Media
10
+ module MimeType
11
+
12
+ def self.mime_group(mime_type)
13
+ mime_type.sub(/\/.*$/,'/') if mime_type # remove everything after /
14
+ end
15
+
16
+ def self.simple(mime_type)
17
+ mime_type.to_s.sub(/\/x\-/,'/') if mime_type # remove x-
18
+ end
19
+
20
+ def self.lookup(mime_type,field)
21
+ (MIME_TYPES[simple(mime_type)]||[])[field]
22
+ end
23
+
24
+ # def self.group_from_mime_type(mime_type)
25
+ # lookup(mime_type,GROUP) || lookup(mime_group(mime_type),GROUP)
26
+ # end
27
+
28
+ def self.icon_for(mtype)
29
+ lookup(mtype,ICON) || lookup(mime_group(mtype),ICON) || lookup('default',ICON)
30
+ end
31
+
32
+ def self.asset_class_from_mime_type(mime_type)
33
+ asset_symbol_from_mime_type(mime_type).to_s.classify
34
+ end
35
+
36
+ def self.asset_symbol_from_mime_type(mime_type)
37
+ lookup(mime_type,ASSET_CLASS) || lookup(mime_group(mime_type),ASSET_CLASS) || lookup('default',ASSET_CLASS)
38
+ end
39
+
40
+ def self.extension_from_mime_type(mime_type)
41
+ lookup(mime_type,EXT)
42
+ end
43
+
44
+ def self.mime_type_from_extension(ext)
45
+ ext = ext.to_s
46
+ ext = File.extname(ext).gsub('.','') if ext =~ /\./
47
+ mimetype = EXTENSIONS[ext]
48
+ if defined?(MIME::Types)
49
+ unless MIME::Types.type_for('.'+ext).empty?
50
+ mimetype ||= MIME::Types.type_for('.'+ext).first.content_type
51
+ end
52
+ end
53
+ mimetype ||= 'application/octet-stream'
54
+ return mimetype
55
+ end
56
+
57
+ #
58
+ # perhaps use http://code.google.com/p/mimetype-fu/
59
+ # for all this?
60
+ def self.type_for(filename)
61
+ self.mime_type_from_extension(filename)
62
+ # todo: add type_from_file_command if ext doesn't pan out.
63
+ end
64
+
65
+ #def type_from_file_command(filename)
66
+ # # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
67
+ # type = (filename.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
68
+ # mime_type = (Paperclip.run("file", "-b --mime-type :file", :file => filename).split(':').last.strip rescue "application/x-#{type}")
69
+ # mime_type = "application/x-#{type}" if mime_type.match(/\(.*?\)/)
70
+ # mime_type
71
+ #end
72
+
73
+ def self.description_from_mime_type(mime_type)
74
+ lookup(mime_type,DESCRIPTION) || lookup(mime_group(mime_type),DESCRIPTION) || lookup('default',DESCRIPTION)
75
+ end
76
+
77
+ def self.compatible_types?(type1, type2)
78
+ (TYPE_ALIASES[type1] || []).include?(type2)
79
+ end
80
+
81
+ EXT = 0; ICON = 1; ASSET_CLASS = 2; DESCRIPTION = 3;
82
+ MIME_TYPES = {
83
+ # mime_type => [file_extension, icon, asset_class, description]
84
+ 'default' => [nil,'default',:asset,'Unknown'],
85
+
86
+ 'text/' => [:txt,:html,:text_asset, 'Text'],
87
+ 'text/plain' => [:txt,:html,:text_asset, 'Text'],
88
+ 'text/html' => [:html,:html,:text_asset, 'Webpage'],
89
+ 'application/rtf' => [:rtf,:rtf,:text_asset, 'Rich Text'],
90
+ 'text/rtf' => [:rtf,:rtf,:text_asset, 'Rich Text'],
91
+ 'text/sgml' => [:sgml,:xml,nil,'XML'],
92
+ 'text/xml' => [:xml,:xml,nil,'XML'],
93
+ 'text/csv' => [:csv,:spreadsheet,:doc_asset, 'Comma Separated Values'],
94
+ 'text/comma-separated-values' => [:csv,:spreadsheet,:doc_asset, 'Comma Separated Values'],
95
+
96
+ 'application/pdf' => [:pdf,:pdf,:image_asset, 'Portable Document Format'],
97
+ 'application/bzpdf' => [:pdf,:pdf,:image_asset, 'Portable Document Format'],
98
+ 'application/gzpdf' => [:pdf,:pdf,:image_asset, 'Portable Document Format'],
99
+ 'application/postscript' => [:ps,:pdf,:image_asset,'Postscript'],
100
+
101
+ 'text/spreadsheet' => [:txt,:spreadsheet,:doc_asset,'Spreadsheet'],
102
+ 'application/gnumeric' => [:gnumeric,:spreadsheet,:doc_asset,'Gnumeric'],
103
+ 'application/kspread' => [:kspread,:spreadsheet,:doc_asset,'KSpread'],
104
+
105
+ 'application/scribus' => [:scribus,:doc,nil,'Scribus'],
106
+ 'application/abiword' => [:abw,:doc,:doc_asset,'Abiword'],
107
+ 'application/kword' => [:kwd,:doc,:doc_asset,'KWord'],
108
+
109
+
110
+ 'application/msword' => [:doc,:msword,:text_asset,'MS Word'],
111
+ 'application/mswrite' => [:doc,:msword,:text_asset,'MS Write'],
112
+ 'application/powerpoint' => [:ppt,:mspowerpoint,:doc_asset,'MS Powerpoint'],
113
+ 'application/excel' => [:xls,:msexcel,:spreadsheet_asset,'MS Excel'],
114
+ 'application/access' => [nil, :msaccess, :doc_asset,'MS Access'],
115
+ 'application/vnd.ms-msword' => [:doc,:msword,:text_asset,'MS Word'],
116
+ 'application/vnd.ms-mswrite' => [:doc,:msword,:text_asset,'MS Write'],
117
+ 'application/vnd.ms-powerpoint' => [:ppt,:mspowerpoint,:doc_asset,'MS Powerpoint'],
118
+ 'application/vnd.ms-excel' => [:xls,:msexcel,:spreadsheet_asset,'MS Excel'],
119
+ 'application/vnd.ms-access' => [nil, :msaccess, :doc_asset,'MS Access'],
120
+ 'application/msword-template' => [:dot,:msword,:text_asset,'MS Word Template'],
121
+ 'application/excel-template' => [:xlt,:msexcel,:spreadsheet_asset,'MS Excel Template'],
122
+ 'application/powerpoint-template' => [:pot,:mspowerpoint,:doc_asset,'MS Powerpoint Template'],
123
+
124
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation' =>
125
+ [:pptx, :mspowerpoint,:doc_asset,'MS Powerpoint'],
126
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation' =>
127
+ [:pptm, :mspowerpoint,:doc_asset,'MS Powerpoint'],
128
+ 'application/vnd.openxmlformats-officedocument.presentationml.template' =>
129
+ [:potx,:mspowerpoint,:doc_asset,'MS Powerpoint Template'],
130
+
131
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' =>
132
+ [:docm,:msword,:text_asset,'MS Word'],
133
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' =>
134
+ [:docx,:msword,:text_asset,'MS Word'],
135
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' =>
136
+ [:dotx,:msword,:text_asset,'MS Word Template'],
137
+
138
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' =>
139
+ [:xlsm,:msexcel,:spreadsheet_asset,'MS Excel'],
140
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' =>
141
+ [:xlsx,:msexcel,:spreadsheet_asset,'MS Excel'],
142
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' =>
143
+ [:xltx,:msexcel,:spreadsheet_asset,'MS Excel Template'],
144
+
145
+ 'application/executable' => [nil,:binary,nil,'Program'],
146
+ 'application/ms-dos-executable' => [nil,:binary,nil,'Program'],
147
+ 'application/octet-stream' => [nil,:binary,nil],
148
+
149
+ 'application/shellscript' => [:sh,:shell,nil,'Script'],
150
+ 'application/ruby' => [:rb,:ruby,nil,'Script'],
151
+
152
+ 'application/vnd.oasis.opendocument.spreadsheet' =>
153
+ [:ods,:oo_spreadsheet,:spreadsheet_asset, 'OpenDocument Spreadsheet'],
154
+ 'application/vnd.oasis.opendocument.formula' =>
155
+ [nil,:oo_spreadsheet,:spreadsheet_asset, 'OpenDocument Formula'],
156
+ 'application/vnd.oasis.opendocument.chart' =>
157
+ [nil,:oo_spreadsheet,:spreadsheet_asset, 'OpenDocument Chart'],
158
+ 'application/vnd.oasis.opendocument.image' =>
159
+ [nil,:oo_graphics, :doc_asset, 'OpenDocument Image'],
160
+ 'application/vnd.oasis.opendocument.graphics' =>
161
+ [:odg,:oo_graphics, :doc_asset, 'OpenDocument Graphics'],
162
+ 'application/vnd.oasis.opendocument.presentation' =>
163
+ [:odp,:oo_presentation,:doc_asset, 'OpenDocument Presentation'],
164
+ 'application/vnd.oasis.opendocument.database' =>
165
+ [:odf,:oo_database,:doc_asset, 'OpenDocument Database'],
166
+ 'application/vnd.oasis.opendocument.text-web' =>
167
+ [:html,:oo_html,:doc_asset, 'OpenDocument Webpage'],
168
+ 'application/vnd.oasis.opendocument.text' =>
169
+ [:odt,:oo_document,:doc_asset, 'OpenDocument Text'],
170
+ 'application/vnd.oasis.opendocument.text-master' =>
171
+ [:odm,:oo_document,:doc_asset, 'OpenDocument Master'],
172
+
173
+ 'application/vnd.oasis.opendocument.presentation-template' =>
174
+ [:otp,:oo_presentation,:doc_asset, 'OpenDocument Presentation'],
175
+ 'application/vnd.oasis.opendocument.graphics-template' =>
176
+ [:otg,:oo_graphics,:doc_asset, 'OpenDocument Graphics'],
177
+ 'application/vnd.oasis.opendocument.spreadsheet-template' =>
178
+ [:ots,:oo_spreadsheet,:spreadsheet_asset, 'OpenDocument Spreadsheet'],
179
+ 'application/vnd.oasis.opendocument.text-template' =>
180
+ [:ott,:oo_document,:doc_asset, 'OpenDocument Text'],
181
+
182
+ 'packages/' => [nil,:archive,nil,'Archive'],
183
+ 'multipart/zip' => [:zip,:archive,nil,'Archive'],
184
+ 'multipart/gzip' => [:gzip,:archive,nil,'Archive'],
185
+ 'multipart/tar' => [:tar,:archive,nil,'Archive'],
186
+ 'application/zip' => [:gzip,:archive,nil,'Archive'],
187
+ 'application/gzip' => [:gzip,:archive,nil,'Archive'],
188
+ 'application/rar' => [:rar,:archive,nil,'Archive'],
189
+ 'application/deb' => [:deb,:archive,nil,'Archive'],
190
+ 'application/tar' => [:tar,:archive,nil,'Archive'],
191
+ 'application/stuffit' => [:sit,:archive,nil,'Archive'],
192
+ 'application/compress' => [nil,:archive,nil,'Archive'],
193
+ 'application/zip-compressed' => [:zip,:archive,nil,'Archive'],
194
+
195
+ 'video/' => [nil,:video,nil,'Video'],
196
+
197
+ 'audio/' => [nil,:audio,:audio_asset,'Audio'],
198
+
199
+ 'image/' => [nil,:image,:image_asset,'Image'],
200
+ 'image/jpeg' => [:jpg,:image,:image_asset, 'JPEG Image'],
201
+ 'image/jpg' => [:jpg,:image,:image_asset, 'JPEG Image'],
202
+ 'image/png' => [:png,:image,:png_asset, 'PNG Image'],
203
+ 'image/gif' => [:png,:image,:gif_asset, 'GIF Image'],
204
+
205
+ 'image/svg+xml' => [:svg,:vector,:svg_asset,'Vector Graphic'],
206
+ 'image/svg+xml-compressed' => [:svg,:vector,:svg_asset,'Vector Graphic'],
207
+ 'application/illustrator' => [:ai,:vector,:image_asset,'Vector Graphic'],
208
+ 'image/bzeps' => [:bzeps,:vector,:image_asset,'Vector Graphic'],
209
+ 'image/eps' => [:eps,:vector,:image_asset,'Vector Graphic'],
210
+ 'image/gzeps' => [:gzeps,:vector,:image_asset,'Vector Graphic'],
211
+
212
+ 'application/pgp-encrypted' => [nil,:lock,nil,'Crypt'],
213
+ 'application/pgp-signature' => [nil,:lock,nil,'Crypt'],
214
+ 'application/pgp-keys' => [nil,:lock,nil,'Crypt']
215
+ }.freeze
216
+
217
+ #
218
+ # This extension mapping is used to force certain mime types.
219
+ # Usually, firefox does pretty good at reporting the correct mime-type,
220
+ # but IE always fails (firefox fails on ogg). So, we use the MIME::Types
221
+ # gem to try to get the correct mime from the extension. Sometimes, however,
222
+ # even this doesn't work. This able will force certain types when
223
+ # MIME::Types fails or is ambiguous
224
+ #
225
+ EXTENSIONS = {
226
+ 'jpg' => 'image/jpeg',
227
+ 'png' => 'image/png',
228
+ 'txt' => 'text/plain',
229
+ 'flv' => 'video/flv',
230
+ 'ogg' => 'audio/ogg',
231
+ 'oga' => 'audio/ogg',
232
+ 'ogv' => 'video/ogg',
233
+ 'pdf' => 'application/pdf',
234
+
235
+ 'doc' => 'application/msword',
236
+ 'xsl' => 'application/excel',
237
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
238
+ 'pptm' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
239
+ 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
240
+ 'docm' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
241
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
242
+ 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
243
+ 'xlsm' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
244
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
245
+ 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
246
+
247
+ 'odt' => 'application/vnd.oasis.opendocument.text',
248
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
249
+ 'odp' => 'application/vnd.oasis.opendocument.presentation',
250
+ 'svg' => 'image/svg+xml',
251
+ 'mod' => 'video/mpeg',
252
+
253
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
254
+ 'pptm' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
255
+ 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
256
+ 'docm' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
257
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
258
+ 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
259
+ 'xlsm' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
260
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
261
+ 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template'
262
+ }.freeze
263
+
264
+ #
265
+ # some types can have multiple names
266
+ #
267
+ TYPE_ALIASES = {
268
+ 'image/jpg' => ['image/jpeg'],
269
+ 'image/jpeg' => ['image/jpg']
270
+ }
271
+ end
272
+ end
@@ -0,0 +1,152 @@
1
+ require 'tempfile'
2
+ require 'fileutils'
3
+ require 'pathname'
4
+
5
+ #
6
+ # media processing requires a different type of tempfile... because we use command
7
+ # line tools to process our temp files, these files can't be open and closed by ruby.
8
+ #
9
+ # instead, Media::TempFile is used to generate closed files from binary data (for files
10
+ # to be fed to command line tools), or to generate empty tmp files (for output filenames
11
+ # to be fed to command line tools).
12
+ #
13
+ # We use the Tempfile class for generating these files, but then we always close them
14
+ # right away. By doing this, we ensure that the temp file will eventually get removed
15
+ # when the Tempfile gets garbage collected.
16
+ #
17
+
18
+ module Media
19
+ class TempFile
20
+
21
+ def self.tempfile_path
22
+ ::Media::TMP_PATH
23
+ end
24
+
25
+ ##
26
+ ## INSTANCE METHODS
27
+ ##
28
+
29
+ public
30
+
31
+ #
32
+ # data may be one of:
33
+ #
34
+ # - FileUpload object: like the kind returned in multibyte encoded file upload forms.
35
+ # - Pathname object: then load data from the file pointed to by the pathname.
36
+ # - IO object: read the contents of the io object, copy to tmp file.
37
+ # - otherwise, dump the contents of the data to the tmp file.
38
+ #
39
+ # if data is empty, we generate an empty one.
40
+ #
41
+ def initialize(data, content_type=nil)
42
+ if data.nil?
43
+ @tmpfile = TempFile.create_from_content_type(content_type)
44
+ elsif data.respond_to?(:path)
45
+ # we are dealing with an uploaded file object
46
+ @tmpfile = TempFile.create_from_file(data.path, content_type, {mode: :move})
47
+ elsif data.is_a?(StringIO)
48
+ data.rewind
49
+ @tmpfile = TempFile.create_from_data(data.read, content_type)
50
+ elsif data.instance_of?(Pathname)
51
+ @tmpfile = TempFile.create_from_file(data.to_s, content_type)
52
+ else
53
+ @tmpfile = TempFile.create_from_data(data, content_type)
54
+ end
55
+ end
56
+
57
+ #
58
+ # like initialize, but if given a block, then it yields the TempFile
59
+ # and also unlinks the file at the end of the block.
60
+ #
61
+ def self.open(data, content_type=nil)
62
+ tmp = TempFile.new(data, content_type)
63
+ if block_given?
64
+ begin
65
+ yield tmp
66
+ ensure
67
+ tmp.clear
68
+ end
69
+ nil
70
+ else
71
+ tmp
72
+ end
73
+ end
74
+
75
+ def clear
76
+ # this is not really needed, because the tmp files are deleted as soon as
77
+ # @tmpfile gets garbage collected.
78
+ # @tmpfile.unlink
79
+ end
80
+
81
+ def any?
82
+ @tmpfile.any?
83
+ end
84
+
85
+ def path
86
+ @tmpfile.path
87
+ end
88
+
89
+ def to_s
90
+ @tmpfile.path
91
+ end
92
+
93
+ ##
94
+ ## CLASS METHODS
95
+ ##
96
+
97
+ private
98
+
99
+ #
100
+ # creates a tempfile filled with the given binary data
101
+ #
102
+ def self.create_from_data(data, content_type=nil)
103
+ tf = new_for_content_type(content_type)
104
+ tf.binmode
105
+ tf.write(data)
106
+ tf.close
107
+ tf
108
+ end
109
+
110
+ #
111
+ # create an empty temp file with an extension to match the content_type
112
+ #
113
+ def self.create_from_content_type(content_type)
114
+ tf = new_for_content_type(content_type)
115
+ tf.close
116
+ tf
117
+ end
118
+
119
+ #
120
+ # create a tmp file that is a copy of another file.
121
+ #
122
+ def self.create_from_file(filepath, content_type, options = {})
123
+ tf = new_for_content_type(content_type)
124
+ tf.close
125
+ if options[:mode] == :move
126
+ FileUtils.mv filepath, tf.path
127
+ else
128
+ FileUtils.cp filepath, tf.path
129
+ end
130
+ tf
131
+ end
132
+
133
+ def self.new_for_content_type(content_type)
134
+ Tempfile.new content_type_basename(content_type),
135
+ tempfile_path,
136
+ mode: 022
137
+ end
138
+
139
+ #
140
+ # create a filename with a file extension from the content_type
141
+ #
142
+ def self.content_type_basename(content_type)
143
+ if content_type
144
+ extension = Media::MimeType.extension_from_mime_type(content_type) || :bin
145
+ ['media_temp_file', ".#{extension}"]
146
+ else
147
+ 'media_temp_file'
148
+ end
149
+ end
150
+
151
+ end
152
+ end