paperclip 2.8.0 → 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of paperclip might be problematic. Click here for more details.
- data/.gitignore +1 -0
- data/.travis.yml +9 -7
- data/Appraisals +6 -12
- data/Gemfile +2 -0
- data/NEWS +24 -0
- data/README.md +53 -21
- data/Rakefile +7 -2
- data/UPGRADING +14 -0
- data/features/basic_integration.feature +8 -8
- data/features/rake_tasks.feature +1 -1
- data/features/step_definitions/attachment_steps.rb +11 -2
- data/features/step_definitions/rails_steps.rb +17 -79
- data/features/support/env.rb +3 -0
- data/features/support/file_helpers.rb +24 -0
- data/features/support/rails.rb +3 -3
- data/gemfiles/{rails3_1.gemfile → 3.0.gemfile} +3 -1
- data/gemfiles/{rails2.gemfile → 3.1.gemfile} +3 -1
- data/gemfiles/{rails3.gemfile → 3.2.gemfile} +3 -1
- data/images.rake +21 -0
- data/lib/generators/paperclip/paperclip_generator.rb +1 -2
- data/lib/paperclip.rb +48 -319
- data/lib/paperclip/attachment.rb +33 -81
- data/lib/paperclip/attachment_options.rb +0 -1
- data/lib/paperclip/callbacks.rb +30 -0
- data/lib/paperclip/errors.rb +27 -0
- data/lib/paperclip/geometry.rb +6 -4
- data/lib/paperclip/glue.rb +15 -0
- data/lib/paperclip/helpers.rb +71 -0
- data/lib/paperclip/instance_methods.rb +35 -0
- data/lib/paperclip/interpolations.rb +2 -2
- data/lib/paperclip/io_adapters/attachment_adapter.rb +62 -0
- data/lib/paperclip/io_adapters/file_adapter.rb +81 -0
- data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
- data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
- data/lib/paperclip/io_adapters/registry.rb +32 -0
- data/lib/paperclip/io_adapters/stringio_adapter.rb +64 -0
- data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +63 -0
- data/lib/paperclip/locales/en.yml +17 -0
- data/lib/paperclip/logger.rb +21 -0
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +1 -1
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +2 -2
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +7 -7
- data/lib/paperclip/processor.rb +32 -17
- data/lib/paperclip/railtie.rb +10 -15
- data/lib/paperclip/storage/filesystem.rb +5 -14
- data/lib/paperclip/storage/fog.rb +2 -21
- data/lib/paperclip/storage/s3.rb +12 -29
- data/lib/paperclip/tempfile.rb +41 -0
- data/lib/paperclip/thumbnail.rb +2 -3
- data/lib/paperclip/validators.rb +45 -0
- data/lib/paperclip/validators/attachment_content_type_validator.rb +47 -0
- data/lib/paperclip/validators/attachment_presence_validator.rb +26 -0
- data/lib/paperclip/validators/attachment_size_validator.rb +102 -0
- data/lib/paperclip/version.rb +1 -1
- data/lib/tasks/paperclip.rake +3 -11
- data/paperclip.gemspec +15 -5
- data/test/adapter_registry_test.rb +32 -0
- data/test/attachment_adapter_test.rb +48 -0
- data/test/attachment_options_test.rb +0 -13
- data/test/attachment_test.rb +27 -55
- data/test/file_adapter_test.rb +43 -0
- data/test/generator_test.rb +78 -0
- data/test/geometry_test.rb +5 -5
- data/test/helper.rb +9 -11
- data/test/identity_adapter_test.rb +8 -0
- data/test/integration_test.rb +39 -94
- data/test/interpolations_test.rb +8 -1
- data/test/matchers/validate_attachment_size_matcher_test.rb +16 -2
- data/test/nil_adapter_test.rb +25 -0
- data/test/paperclip_test.rb +30 -189
- data/test/storage/filesystem_test.rb +0 -14
- data/test/storage/fog_test.rb +0 -14
- data/test/storage/s3_live_test.rb +22 -9
- data/test/storage/s3_test.rb +70 -34
- data/test/stringio_adapter_test.rb +42 -0
- data/test/style_test.rb +10 -16
- data/test/thumbnail_test.rb +16 -10
- data/test/uploaded_file_adapter_test.rb +98 -0
- data/test/validators/attachment_content_type_validator_test.rb +140 -0
- data/test/validators/attachment_presence_validator_test.rb +85 -0
- data/test/validators/attachment_size_validator_test.rb +207 -0
- data/test/validators_test.rb +25 -0
- metadata +152 -30
- data/gemfiles/rails3_2.gemfile +0 -9
- data/generators/paperclip/USAGE +0 -5
- data/generators/paperclip/paperclip_generator.rb +0 -27
- data/generators/paperclip/templates/paperclip_migration.rb.erb +0 -19
- data/init.rb +0 -4
- data/lib/paperclip/callback_compatibility.rb +0 -61
- data/lib/paperclip/iostream.rb +0 -45
- data/lib/paperclip/upfile.rb +0 -64
- data/rails/init.rb +0 -2
- data/test/iostream_test.rb +0 -71
- data/test/upfile_test.rb +0 -53
data/lib/paperclip/attachment.rb
CHANGED
@@ -7,8 +7,6 @@ module Paperclip
|
|
7
7
|
# when the model saves, deletes when the model is destroyed, and processes
|
8
8
|
# the file upon assignment.
|
9
9
|
class Attachment
|
10
|
-
include IOStream
|
11
|
-
|
12
10
|
def self.default_options
|
13
11
|
@default_options ||= {
|
14
12
|
:convert_options => {},
|
@@ -25,7 +23,7 @@ module Paperclip
|
|
25
23
|
:source_file_options => {},
|
26
24
|
:storage => :filesystem,
|
27
25
|
:styles => {},
|
28
|
-
:url => "/system/:attachment/:
|
26
|
+
:url => "/system/:class/:attachment/:id_partition/:style/:filename",
|
29
27
|
:url_generator => Paperclip::UrlGenerator,
|
30
28
|
:use_default_time_zone => true,
|
31
29
|
:use_timestamp => true,
|
@@ -90,29 +88,16 @@ module Paperclip
|
|
90
88
|
# new_user.avatar = old_user.avatar
|
91
89
|
def assign uploaded_file
|
92
90
|
ensure_required_accessors!
|
91
|
+
file = Paperclip.io_adapters.for(uploaded_file)
|
93
92
|
|
94
|
-
if uploaded_file.is_a?(Paperclip::Attachment)
|
95
|
-
uploaded_filename = uploaded_file.original_filename
|
96
|
-
uploaded_file = uploaded_file.to_file(:original)
|
97
|
-
close_uploaded_file = uploaded_file.respond_to?(:close)
|
98
|
-
else
|
99
|
-
instance_write(:uploaded_file, uploaded_file) if uploaded_file
|
100
|
-
end
|
101
|
-
|
102
|
-
return nil unless valid_assignment?(uploaded_file)
|
103
|
-
|
104
|
-
uploaded_file.binmode if uploaded_file.respond_to? :binmode
|
105
93
|
self.clear
|
94
|
+
return nil if file.nil?
|
106
95
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
instance_write(:file_name, cleanup_filename(uploaded_filename.strip))
|
113
|
-
instance_write(:content_type, uploaded_file.content_type.to_s.strip)
|
114
|
-
instance_write(:file_size, uploaded_file.size.to_i)
|
115
|
-
instance_write(:fingerprint, generate_fingerprint(uploaded_file)) if stores_fingerprint
|
96
|
+
@queued_for_write[:original] = file
|
97
|
+
instance_write(:file_name, cleanup_filename(file.original_filename))
|
98
|
+
instance_write(:content_type, file.content_type)
|
99
|
+
instance_write(:file_size, file.size)
|
100
|
+
instance_write(:fingerprint, file.fingerprint) if instance_respond_to?(:fingerprint)
|
116
101
|
instance_write(:updated_at, Time.now)
|
117
102
|
|
118
103
|
@dirty = true
|
@@ -120,10 +105,8 @@ module Paperclip
|
|
120
105
|
post_process(*@options[:only_process]) if post_processing
|
121
106
|
|
122
107
|
# Reset the file size if the original file was reprocessed.
|
123
|
-
instance_write(:file_size, @queued_for_write[:original].size
|
124
|
-
instance_write(:fingerprint,
|
125
|
-
ensure
|
126
|
-
uploaded_file.close if close_uploaded_file
|
108
|
+
instance_write(:file_size, @queued_for_write[:original].size)
|
109
|
+
instance_write(:fingerprint, @queued_for_write[:original].fingerprint) if instance_respond_to?(:fingerprint)
|
127
110
|
end
|
128
111
|
|
129
112
|
# Returns the public URL of the attachment with a given style. This does
|
@@ -252,16 +235,10 @@ module Paperclip
|
|
252
235
|
instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
|
253
236
|
end
|
254
237
|
|
255
|
-
# Returns the
|
256
|
-
# <attachment>
|
238
|
+
# Returns the fingerprint of the file, if one's defined. The fingerprint is
|
239
|
+
# stored in the <attachment>_fingerpring attribute of the model.
|
257
240
|
def fingerprint
|
258
|
-
|
259
|
-
instance_read(:fingerprint)
|
260
|
-
elsif @instance.respond_to?("#{name}_fingerprint".to_sym)
|
261
|
-
@queued_for_write[:original] && generate_fingerprint(@queued_for_write[:original])
|
262
|
-
else
|
263
|
-
nil
|
264
|
-
end
|
241
|
+
instance_read(:fingerprint)
|
265
242
|
end
|
266
243
|
|
267
244
|
# Returns the content_type of the file as originally assigned, and lives
|
@@ -292,53 +269,21 @@ module Paperclip
|
|
292
269
|
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@options[:hash_digest]).new, @options[:hash_secret], data)
|
293
270
|
end
|
294
271
|
|
295
|
-
def generate_fingerprint(source)
|
296
|
-
if source.respond_to?(:path) && source.path && !source.path.blank?
|
297
|
-
Digest::MD5.file(source.path).to_s
|
298
|
-
else
|
299
|
-
data = source.read
|
300
|
-
source.rewind if source.respond_to?(:rewind)
|
301
|
-
Digest::MD5.hexdigest(data)
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
# Paths and URLs can have a number of variables interpolated into them
|
306
|
-
# to vary the storage location based on name, id, style, class, etc.
|
307
|
-
# This method is a deprecated access into supplying and retrieving these
|
308
|
-
# interpolations. Future access should use either Paperclip.interpolates
|
309
|
-
# or extend the Paperclip::Interpolations module directly.
|
310
|
-
def self.interpolations
|
311
|
-
warn('[DEPRECATION] Paperclip::Attachment.interpolations is deprecated ' +
|
312
|
-
'and will be removed from future versions. ' +
|
313
|
-
'Use Paperclip.interpolates instead')
|
314
|
-
Paperclip::Interpolations
|
315
|
-
end
|
316
|
-
|
317
272
|
# This method really shouldn't be called that often. It's expected use is
|
318
273
|
# in the paperclip:refresh rake task and that's it. It will regenerate all
|
319
274
|
# thumbnails forcefully, by reobtaining the original file and going through
|
320
275
|
# the post-process again.
|
321
276
|
def reprocess!(*style_args)
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
new_original.write( old_original.respond_to?(:get) ? old_original.get : old_original.read )
|
326
|
-
new_original.rewind
|
327
|
-
|
328
|
-
@queued_for_write = { :original => new_original }
|
329
|
-
instance_write(:updated_at, Time.now)
|
330
|
-
post_process(*style_args)
|
331
|
-
|
332
|
-
old_original.close if old_original.respond_to?(:close)
|
333
|
-
old_original.unlink if old_original.respond_to?(:unlink)
|
334
|
-
|
277
|
+
saved_only_process, @options[:only_process] = @options[:only_process], style_args
|
278
|
+
begin
|
279
|
+
assign(self)
|
335
280
|
save
|
336
|
-
|
337
|
-
|
281
|
+
rescue Errno::EACCES => e
|
282
|
+
warn "#{e} - skipping file."
|
283
|
+
false
|
284
|
+
ensure
|
285
|
+
@options[:only_process] = saved_only_process
|
338
286
|
end
|
339
|
-
rescue Errno::EACCES => e
|
340
|
-
warn "#{e} - skipping file"
|
341
|
-
false
|
342
287
|
end
|
343
288
|
|
344
289
|
# Returns true if a file has been assigned.
|
@@ -348,6 +293,12 @@ module Paperclip
|
|
348
293
|
|
349
294
|
alias :present? :file?
|
350
295
|
|
296
|
+
# Determines whether the instance responds to this attribute. Used to prevent
|
297
|
+
# calculations on fields we won't even store.
|
298
|
+
def instance_respond_to?(attr)
|
299
|
+
instance.respond_to?(:"#{name}_#{attr}")
|
300
|
+
end
|
301
|
+
|
351
302
|
# Writes the attachment-specific attribute on the instance. For example,
|
352
303
|
# instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance's
|
353
304
|
# "avatar_file_name" field (assuming the attachment is called avatar).
|
@@ -377,7 +328,7 @@ module Paperclip
|
|
377
328
|
def ensure_required_accessors! #:nodoc:
|
378
329
|
%w(file_name).each do |field|
|
379
330
|
unless @instance.respond_to?("#{name}_#{field}") && @instance.respond_to?("#{name}_#{field}=")
|
380
|
-
raise
|
331
|
+
raise Paperclip::Error.new("#{@instance.class} model missing required attr_accessor for '#{name}_#{field}'")
|
381
332
|
end
|
382
333
|
end
|
383
334
|
end
|
@@ -395,7 +346,7 @@ module Paperclip
|
|
395
346
|
begin
|
396
347
|
storage_module = Paperclip::Storage.const_get(storage_class_name)
|
397
348
|
rescue NameError
|
398
|
-
raise StorageMethodNotFound, "Cannot load storage module '#{storage_class_name}'"
|
349
|
+
raise Errors::StorageMethodNotFound, "Cannot load storage module '#{storage_class_name}'"
|
399
350
|
end
|
400
351
|
self.extend(storage_module)
|
401
352
|
end
|
@@ -440,7 +391,8 @@ module Paperclip
|
|
440
391
|
@queued_for_write[name] = style.processors.inject(@queued_for_write[:original]) do |file, processor|
|
441
392
|
Paperclip.processor(processor).make(file, style.processor_options, self)
|
442
393
|
end
|
443
|
-
|
394
|
+
@queued_for_write[name] = Paperclip.io_adapters.for(@queued_for_write[name])
|
395
|
+
rescue Paperclip::Error => e
|
444
396
|
log("An error was received while processing: #{e.inspect}")
|
445
397
|
(@errors[:processing] ||= []) << e.message if @options[:whiny]
|
446
398
|
end
|
@@ -475,8 +427,8 @@ module Paperclip
|
|
475
427
|
# called by storage after the writes are flushed and before @queued_for_writes is cleared
|
476
428
|
def after_flush_writes
|
477
429
|
@queued_for_write.each do |style, file|
|
478
|
-
file.close unless file.closed?
|
479
|
-
file.unlink if file.respond_to?(:unlink) && file.path.present? && File.exist?(file.path)
|
430
|
+
# file.close unless file.closed?
|
431
|
+
# file.unlink if file.respond_to?(:unlink) && file.path.present? && File.exist?(file.path)
|
480
432
|
end
|
481
433
|
end
|
482
434
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Paperclip
|
2
|
+
module Callbacks
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(Defining)
|
5
|
+
base.send(:include, Running)
|
6
|
+
end
|
7
|
+
|
8
|
+
module Defining
|
9
|
+
def define_paperclip_callbacks(*callbacks)
|
10
|
+
define_callbacks *[callbacks, {:terminator => "result == false"}].flatten
|
11
|
+
callbacks.each do |callback|
|
12
|
+
eval <<-end_callbacks
|
13
|
+
def before_#{callback}(*args, &blk)
|
14
|
+
set_callback(:#{callback}, :before, *args, &blk)
|
15
|
+
end
|
16
|
+
def after_#{callback}(*args, &blk)
|
17
|
+
set_callback(:#{callback}, :after, *args, &blk)
|
18
|
+
end
|
19
|
+
end_callbacks
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module Running
|
25
|
+
def run_paperclip_callbacks(callback, opts = nil, &block)
|
26
|
+
run_callbacks(callback, opts, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Paperclip
|
2
|
+
# A base error class for Paperclip. Most of the error that will be thrown
|
3
|
+
# from Paperclip will inherits from this class.
|
4
|
+
class Error < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
module Errors
|
8
|
+
# Will be thrown when a storage method is not found.
|
9
|
+
class StorageMethodNotFound < Paperclip::Error
|
10
|
+
end
|
11
|
+
|
12
|
+
# Will be thrown when a command or executable is not found.
|
13
|
+
class CommandNotFoundError < Paperclip::Error
|
14
|
+
end
|
15
|
+
|
16
|
+
# Will be thrown when ImageMagic cannot determine the uploaded file's
|
17
|
+
# metadata, usually this would mean the file is not an image.
|
18
|
+
class NotIdentifiedByImageMagickError < Paperclip::Error
|
19
|
+
end
|
20
|
+
|
21
|
+
# Will be thrown if the interpolation is creating an infinite loop. If you
|
22
|
+
# are creating an interpolator which might cause an infinite loop, you
|
23
|
+
# should be throwing this error upon the infinite loop as well.
|
24
|
+
class InfiniteInterpolationError < Paperclip::Error
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/paperclip/geometry.rb
CHANGED
@@ -17,16 +17,18 @@ module Paperclip
|
|
17
17
|
# a Tempfile object, which would be eligible for file deletion when no longer referenced.
|
18
18
|
def self.from_file file
|
19
19
|
file_path = file.respond_to?(:path) ? file.path : file
|
20
|
-
raise(
|
20
|
+
raise(Errors::NotIdentifiedByImageMagickError.new("Cannot find the geometry of a file with a blank name")) if file_path.blank?
|
21
21
|
geometry = begin
|
22
|
-
|
22
|
+
silence_stream(STDERR) do
|
23
|
+
Paperclip.run("identify", "-format %wx%h :file", :file => "#{file_path}[0]")
|
24
|
+
end
|
23
25
|
rescue Cocaine::ExitStatusError
|
24
26
|
""
|
25
27
|
rescue Cocaine::CommandNotFoundError => e
|
26
|
-
raise
|
28
|
+
raise Errors::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
|
27
29
|
end
|
28
30
|
parse(geometry) ||
|
29
|
-
raise(NotIdentifiedByImageMagickError.new("#{file_path} is not recognized by the 'identify' command."))
|
31
|
+
raise(Errors::NotIdentifiedByImageMagickError.new("#{file_path} is not recognized by the 'identify' command."))
|
30
32
|
end
|
31
33
|
|
32
34
|
# Parses a "WxH" formatted string, where W is the width and H is the height.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'paperclip/callbacks'
|
2
|
+
|
3
|
+
module Paperclip
|
4
|
+
module Glue
|
5
|
+
def self.included base #:nodoc:
|
6
|
+
base.extend ClassMethods
|
7
|
+
base.send :include, Callbacks
|
8
|
+
base.send :include, Validators
|
9
|
+
base.class_attribute :attachment_definitions
|
10
|
+
|
11
|
+
locale_path = Dir.glob(File.dirname(__FILE__) + "/locales/*.{rb,yml}")
|
12
|
+
I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Paperclip
|
2
|
+
module Helpers
|
3
|
+
def configure
|
4
|
+
yield(self) if block_given?
|
5
|
+
end
|
6
|
+
|
7
|
+
def interpolates key, &block
|
8
|
+
Paperclip::Interpolations[key] = block
|
9
|
+
end
|
10
|
+
|
11
|
+
# The run method takes the name of a binary to run, the arguments to that binary
|
12
|
+
# and some options:
|
13
|
+
#
|
14
|
+
# :command_path -> A $PATH-like variable that defines where to look for the binary
|
15
|
+
# on the filesystem. Colon-separated, just like $PATH.
|
16
|
+
#
|
17
|
+
# :expected_outcodes -> An array of integers that defines the expected exit codes
|
18
|
+
# of the binary. Defaults to [0].
|
19
|
+
#
|
20
|
+
# :log_command -> Log the command being run when set to true (defaults to false).
|
21
|
+
# This will only log if logging in general is set to true as well.
|
22
|
+
#
|
23
|
+
# :swallow_stderr -> Set to true if you don't care what happens on STDERR.
|
24
|
+
#
|
25
|
+
def run(cmd, arguments = "", local_options = {})
|
26
|
+
command_path = options[:command_path]
|
27
|
+
Cocaine::CommandLine.path = ( Cocaine::CommandLine.path ? [Cocaine::CommandLine.path].flatten | [command_path] : command_path )
|
28
|
+
local_options = local_options.merge(:logger => logger) if logging? && (options[:log_command] || local_options[:log_command])
|
29
|
+
Cocaine::CommandLine.new(cmd, arguments, local_options).run
|
30
|
+
end
|
31
|
+
|
32
|
+
# Find all instances of the given Active Record model +klass+ with attachment +name+.
|
33
|
+
# This method is used by the refresh rake tasks.
|
34
|
+
def each_instance_with_attachment(klass, name)
|
35
|
+
unscope_method = class_for(klass).respond_to?(:unscoped) ? :unscoped : :with_exclusive_scope
|
36
|
+
class_for(klass).send(unscope_method) do
|
37
|
+
class_for(klass).find_each(:conditions => "#{name}_file_name is not null") do |instance|
|
38
|
+
yield(instance)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def class_for(class_name)
|
44
|
+
# Ruby 1.9 introduces an inherit argument for Module#const_get and
|
45
|
+
# #const_defined? and changes their default behavior.
|
46
|
+
# https://github.com/rails/rails/blob/v3.0.9/activesupport/lib/active_support/inflector/methods.rb#L89
|
47
|
+
if Module.method(:const_get).arity == 1
|
48
|
+
class_name.split('::').inject(Object) do |klass, partial_class_name|
|
49
|
+
klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name) : klass.const_missing(partial_class_name)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
class_name.split('::').inject(Object) do |klass, partial_class_name|
|
53
|
+
klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name, false) : klass.const_missing(partial_class_name)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def check_for_url_clash(name,url,klass)
|
59
|
+
@names_url ||= {}
|
60
|
+
default_url = url || Attachment.default_options[:url]
|
61
|
+
if @names_url[name] && @names_url[name][:url] == default_url && @names_url[name][:class] != klass && @names_url[name][:url] !~ /:class/
|
62
|
+
log("Duplicate URL for #{name} with #{default_url}. This will clash with attachment defined in #{@names_url[name][:class]} class")
|
63
|
+
end
|
64
|
+
@names_url[name] = {:url => default_url, :class => klass}
|
65
|
+
end
|
66
|
+
|
67
|
+
def reset_duplicate_clash_check!
|
68
|
+
@names_url = nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Paperclip
|
2
|
+
module InstanceMethods #:nodoc:
|
3
|
+
def attachment_for name
|
4
|
+
@_paperclip_attachments ||= {}
|
5
|
+
@_paperclip_attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name])
|
6
|
+
end
|
7
|
+
|
8
|
+
def each_attachment
|
9
|
+
self.class.attachment_definitions.each do |name, definition|
|
10
|
+
yield(name, attachment_for(name))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def save_attached_files
|
15
|
+
Paperclip.log("Saving attachments.")
|
16
|
+
each_attachment do |name, attachment|
|
17
|
+
attachment.send(:save)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy_attached_files
|
22
|
+
Paperclip.log("Deleting attachments.")
|
23
|
+
each_attachment do |name, attachment|
|
24
|
+
attachment.send(:flush_deletes)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def prepare_for_destroy
|
29
|
+
Paperclip.log("Scheduling attachments for deletion.")
|
30
|
+
each_attachment do |name, attachment|
|
31
|
+
attachment.send(:queue_existing_for_delete)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -46,7 +46,7 @@ module Paperclip
|
|
46
46
|
# is used in the default :path to ease default specifications.
|
47
47
|
RIGHT_HERE = "#{__FILE__.gsub(%r{^\./}, "")}:#{__LINE__ + 3}"
|
48
48
|
def url attachment, style_name
|
49
|
-
raise InfiniteInterpolationError if caller.any?{|b| b.index(RIGHT_HERE) }
|
49
|
+
raise Errors::InfiniteInterpolationError if caller.any?{|b| b.index(RIGHT_HERE) }
|
50
50
|
attachment.url(style_name, :timestamp => false, :escape => false)
|
51
51
|
end
|
52
52
|
|
@@ -93,7 +93,7 @@ module Paperclip
|
|
93
93
|
# If the style has a format defined, it will return the format instead
|
94
94
|
# of the actual extension.
|
95
95
|
def extension attachment, style_name
|
96
|
-
((style = attachment.styles[style_name.to_sym]) && style[:format]) ||
|
96
|
+
((style = attachment.styles[style_name.to_s.to_sym]) && style[:format]) ||
|
97
97
|
File.extname(attachment.original_filename).gsub(/^\.+/, "")
|
98
98
|
end
|
99
99
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class AttachmentAdapter
|
3
|
+
|
4
|
+
def initialize(target)
|
5
|
+
@target = target
|
6
|
+
cache_current_values
|
7
|
+
end
|
8
|
+
|
9
|
+
def original_filename
|
10
|
+
@original_filename
|
11
|
+
end
|
12
|
+
|
13
|
+
def content_type
|
14
|
+
@content_type
|
15
|
+
end
|
16
|
+
|
17
|
+
def size
|
18
|
+
@size
|
19
|
+
end
|
20
|
+
|
21
|
+
def nil?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def fingerprint
|
26
|
+
@fingerprint ||= Digest::MD5.file(path).to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def read(length = nil, buffer = nil)
|
30
|
+
@tempfile.read(length, buffer)
|
31
|
+
end
|
32
|
+
|
33
|
+
def eof?
|
34
|
+
@tempfile.eof?
|
35
|
+
end
|
36
|
+
|
37
|
+
def path
|
38
|
+
@tempfile.path
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def cache_current_values
|
44
|
+
@tempfile = copy_to_tempfile(@target)
|
45
|
+
@original_filename = @target.original_filename
|
46
|
+
@content_type = @target.content_type
|
47
|
+
@size = @tempfile.size || @target.size
|
48
|
+
end
|
49
|
+
|
50
|
+
def copy_to_tempfile(src)
|
51
|
+
dest = Tempfile.new(src.original_filename)
|
52
|
+
dest.binmode
|
53
|
+
FileUtils.cp(src.path(:original), dest.path)
|
54
|
+
dest
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Paperclip.io_adapters.register Paperclip::AttachmentAdapter do |target|
|
61
|
+
Paperclip::Attachment === target
|
62
|
+
end
|