paperclip 2.3.11 → 2.3.16

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.

@@ -30,8 +30,9 @@ module Paperclip
30
30
  # (which method (in the attachment) will call any supplied procs)
31
31
  # There is an important change of interface here: a style rule can set its own processors
32
32
  # by default we behave as before, though.
33
+ # if a proc has been supplied, we call it here
33
34
  def processors
34
- @processors || attachment.processors
35
+ @processors.respond_to?(:call) ? @processors.call(attachment.instance) : (@processors || attachment.processors)
35
36
  end
36
37
 
37
38
  # retrieves from the attachment the whiny setting
@@ -71,7 +72,7 @@ module Paperclip
71
72
  # Supports getting and setting style properties with hash notation to ensure backwards-compatibility
72
73
  # eg. @attachment.styles[:large][:geometry]@ will still work
73
74
  def [](key)
74
- if [:name, :convert_options, :whiny, :processors, :geometry, :format].include?(key)
75
+ if [:name, :convert_options, :whiny, :processors, :geometry, :format, :animated].include?(key)
75
76
  send(key)
76
77
  elsif defined? @other_args[key]
77
78
  @other_args[key]
@@ -79,7 +80,7 @@ module Paperclip
79
80
  end
80
81
 
81
82
  def []=(key, value)
82
- if [:name, :convert_options, :whiny, :processors, :geometry, :format].include?(key)
83
+ if [:name, :convert_options, :whiny, :processors, :geometry, :format, :animated].include?(key)
83
84
  send("#{key}=".intern, value)
84
85
  else
85
86
  @other_args[key] = value
@@ -2,7 +2,11 @@ module Paperclip
2
2
  # Handles thumbnailing images that are uploaded.
3
3
  class Thumbnail < Processor
4
4
 
5
- attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options, :source_file_options
5
+ attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options,
6
+ :source_file_options, :animated
7
+
8
+ # List of formats that we need to preserve animation
9
+ ANIMATED_FORMATS = %w(gif)
6
10
 
7
11
  # Creates a Thumbnail object set to work on the +file+ given. It
8
12
  # will attempt to transform the image into one defined by +target_geometry+
@@ -22,6 +26,7 @@ module Paperclip
22
26
  @convert_options = options[:convert_options]
23
27
  @whiny = options[:whiny].nil? ? true : options[:whiny]
24
28
  @format = options[:format]
29
+ @animated = options[:animated].nil? ? true : options[:animated]
25
30
 
26
31
  @source_file_options = @source_file_options.split(/\s+/) if @source_file_options.respond_to?(:split)
27
32
  @convert_options = @convert_options.split(/\s+/) if @convert_options.respond_to?(:split)
@@ -58,9 +63,11 @@ module Paperclip
58
63
 
59
64
  parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")
60
65
 
61
- success = Paperclip.run("convert", parameters, :source => "#{File.expand_path(src.path)}[0]", :dest => File.expand_path(dst.path))
62
- rescue PaperclipCommandLineError => e
66
+ success = Paperclip.run("convert", parameters, :source => "#{File.expand_path(src.path)}#{'[0]' unless animated?}", :dest => File.expand_path(dst.path))
67
+ rescue Cocaine::ExitStatusError => e
63
68
  raise PaperclipError, "There was an error processing the thumbnail for #{@basename}" if @whiny
69
+ rescue Cocaine::CommandNotFoundError => e
70
+ raise Paperclip::CommandNotFoundError.new("Could not run the `convert` command. Please install ImageMagick.")
64
71
  end
65
72
 
66
73
  dst
@@ -71,9 +78,17 @@ module Paperclip
71
78
  def transformation_command
72
79
  scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
73
80
  trans = []
81
+ trans << "-coalesce" if animated?
74
82
  trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
75
83
  trans << "-crop" << %["#{crop}"] << "+repage" if crop
76
84
  trans
77
85
  end
86
+
87
+ protected
88
+
89
+ # Return true if the format is animated
90
+ def animated?
91
+ @animated && ANIMATED_FORMATS.include?(@current_format[1..-1]) && (ANIMATED_FORMATS.include?(@format.to_s) || @format.blank?)
92
+ end
78
93
  end
79
94
  end
@@ -1,3 +1,5 @@
1
+ require 'mime/types'
2
+
1
3
  module Paperclip
2
4
  # The Upfile module is a convenience module for adding uploaded-file-type methods
3
5
  # to the +File+ class. Useful for testing.
@@ -6,23 +8,28 @@ module Paperclip
6
8
 
7
9
  # Infer the MIME-type of the file from the extension.
8
10
  def content_type
9
- type = (self.path.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
10
- case type
11
- when %r"jp(e|g|eg)" then "image/jpeg"
12
- when %r"tiff?" then "image/tiff"
13
- when %r"png", "gif", "bmp" then "image/#{type}"
14
- when "txt" then "text/plain"
15
- when %r"html?" then "text/html"
16
- when "js" then "application/js"
17
- when "csv", "xml", "css" then "text/#{type}"
11
+ types = MIME::Types.type_for(self.original_filename)
12
+ if types.length == 0
13
+ type_from_file_command
14
+ elsif types.length == 1
15
+ types.first.content_type
18
16
  else
19
- # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
20
- content_type = (Paperclip.run("file", "-b --mime-type :file", :file => self.path).split(':').last.strip rescue "application/x-#{type}")
21
- content_type = "application/x-#{type}" if content_type.match(/\(.*?\)/)
22
- content_type
17
+ iterate_over_array_to_find_best_option(types)
23
18
  end
24
19
  end
25
20
 
21
+ def iterate_over_array_to_find_best_option(types)
22
+ types.reject {|type| type.content_type.match(/\/x-/) }.first
23
+ end
24
+
25
+ def type_from_file_command
26
+ # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
27
+ type = (self.original_filename.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
28
+ mime_type = (Paperclip.run("file", "-b --mime-type :file", :file => self.path).split(':').last.strip rescue "application/x-#{type}")
29
+ mime_type = "application/x-#{type}" if mime_type.match(/\(.*?\)/)
30
+ mime_type
31
+ end
32
+
26
33
  # Returns the file's normal name.
27
34
  def original_filename
28
35
  File.basename(self.path)
@@ -1,3 +1,3 @@
1
1
  module Paperclip
2
- VERSION = "2.3.11" unless defined? Paperclip::VERSION
2
+ VERSION = "2.3.16" unless defined? Paperclip::VERSION
3
3
  end
data/lib/paperclip.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # columns to your table.
6
6
  #
7
7
  # Author:: Jon Yurek
8
- # Copyright:: Copyright (c) 2008-2009 thoughtbot, inc.
8
+ # Copyright:: Copyright (c) 2008-2011 thoughtbot, inc.
9
9
  # License:: MIT License (http://www.opensource.org/licenses/mit-license.php)
10
10
  #
11
11
  # Paperclip defines an attachment as any file, though it makes special considerations
@@ -38,14 +38,9 @@ require 'paperclip/interpolations'
38
38
  require 'paperclip/style'
39
39
  require 'paperclip/attachment'
40
40
  require 'paperclip/storage'
41
- require 'paperclip/callback_compatability'
42
- require 'paperclip/command_line'
41
+ require 'paperclip/callback_compatibility'
43
42
  require 'paperclip/railtie'
44
- if defined?(Rails.root) && Rails.root
45
- Dir.glob(File.join(File.expand_path(Rails.root), "lib", "paperclip_processors", "*.rb")).each do |processor|
46
- require processor
47
- end
48
- end
43
+ require 'cocaine'
49
44
 
50
45
  # The base module that gets included in ActiveRecord::Base. See the
51
46
  # documentation for Paperclip::ClassMethods for more useful information.
@@ -87,7 +82,7 @@ module Paperclip
87
82
  # symlink them so they are all in the same directory.
88
83
  #
89
84
  # If the command returns with a result code that is not one of the
90
- # expected_outcodes, a PaperclipCommandLineError will be raised. Generally
85
+ # expected_outcodes, a Cocaine::CommandLineError will be raised. Generally
91
86
  # a code of 0 is expected, but a list of codes may be passed if necessary.
92
87
  # These codes should be passed as a hash as the last argument, like so:
93
88
  #
@@ -100,12 +95,13 @@ module Paperclip
100
95
  if options[:image_magick_path]
101
96
  Paperclip.log("[DEPRECATION] :image_magick_path is deprecated and will be removed. Use :command_path instead")
102
97
  end
103
- CommandLine.path = options[:command_path] || options[:image_magick_path]
104
- CommandLine.new(cmd, *params).run
98
+ Cocaine::CommandLine.path = options[:command_path] || options[:image_magick_path]
99
+ Cocaine::CommandLine.new(cmd, *params).run
105
100
  end
106
101
 
107
102
  def processor name #:nodoc:
108
103
  name = name.to_s.camelize
104
+ load_processor(name) unless Paperclip.const_defined?(name)
109
105
  processor = Paperclip.const_get(name)
110
106
  unless processor.ancestors.include?(Paperclip::Processor)
111
107
  raise PaperclipError.new("Processor #{name} was not found")
@@ -113,8 +109,14 @@ module Paperclip
113
109
  processor
114
110
  end
115
111
 
112
+ def load_processor(name)
113
+ if defined?(Rails.root) && Rails.root
114
+ require File.expand_path(Rails.root.join("lib", "paperclip_processors", "#{name.underscore}.rb"))
115
+ end
116
+ end
117
+
116
118
  def each_instance_with_attachment(klass, name)
117
- Object.const_get(klass).all.each do |instance|
119
+ class_for(klass).all.each do |instance|
118
120
  yield(instance) if instance.send(:"#{name}?")
119
121
  end
120
122
  end
@@ -126,23 +128,40 @@ module Paperclip
126
128
  end
127
129
 
128
130
  def logger #:nodoc:
129
- ActiveRecord::Base.logger
131
+ defined?(ActiveRecord::Base) ? ActiveRecord::Base.logger : Rails.logger
130
132
  end
131
133
 
132
134
  def logging? #:nodoc:
133
135
  options[:log]
134
136
  end
135
- end
136
137
 
137
- class PaperclipError < StandardError #:nodoc:
138
+ def class_for(class_name)
139
+ # Ruby 1.9 introduces an inherit argument for Module#const_get and
140
+ # #const_defined? and changes their default behavior.
141
+ # https://github.com/rails/rails/blob/v3.0.9/activesupport/lib/active_support/inflector/methods.rb#L89
142
+ if Module.method(:const_get).arity == 1
143
+ class_name.split('::').inject(Object) do |klass, partial_class_name|
144
+ klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name) : klass.const_missing(partial_class_name)
145
+ end
146
+ else
147
+ class_name.split('::').inject(Object) do |klass, partial_class_name|
148
+ klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name, false) : klass.const_missing(partial_class_name)
149
+ end
150
+ end
151
+ rescue ArgumentError => e
152
+ # Sadly, we need to capture ArguementError here because Rails 2.3.x
153
+ # Active Support dependency's management will try to the constant inherited
154
+ # from Object, and fail misably with "Object is not missing constant X" error
155
+ # https://github.com/rails/rails/blob/v2.3.12/activesupport/lib/active_support/dependencies.rb#L124
156
+ if e.message =~ /is not missing constant/
157
+ raise NameError, "uninitialized constant #{class_name}"
158
+ else
159
+ raise e
160
+ end
161
+ end
138
162
  end
139
163
 
140
- class PaperclipCommandLineError < PaperclipError #:nodoc:
141
- attr_accessor :output
142
- def initialize(msg = nil, output = nil)
143
- super(msg)
144
- @output = output
145
- end
164
+ class PaperclipError < StandardError #:nodoc:
146
165
  end
147
166
 
148
167
  class StorageMethodNotFound < PaperclipError
@@ -160,7 +179,8 @@ module Paperclip
160
179
  module Glue
161
180
  def self.included base #:nodoc:
162
181
  base.extend ClassMethods
163
- if base.respond_to?("set_callback")
182
+ base.class_attribute :attachment_definitions if base.respond_to?(:class_attribute)
183
+ if base.respond_to?(:set_callback)
164
184
  base.send :include, Paperclip::CallbackCompatability::Rails3
165
185
  else
166
186
  base.send :include, Paperclip::CallbackCompatability::Rails21
@@ -224,7 +244,7 @@ module Paperclip
224
244
  # }
225
245
  # NOTE: While not deprecated yet, it is not recommended to specify options this way.
226
246
  # It is recommended that :convert_options option be included in the hash passed to each
227
- # :styles for compatability with future versions.
247
+ # :styles for compatibility with future versions.
228
248
  # NOTE: Strings supplied to :convert_options are split on space in order to undergo
229
249
  # shell quoting for safety. If your options require a space, please pre-split them
230
250
  # and pass an array to :convert_options instead.
@@ -235,7 +255,14 @@ module Paperclip
235
255
  def has_attached_file name, options = {}
236
256
  include InstanceMethods
237
257
 
238
- write_inheritable_attribute(:attachment_definitions, {}) if attachment_definitions.nil?
258
+ if attachment_definitions.nil?
259
+ if respond_to?(:class_attribute)
260
+ self.attachment_definitions = {}
261
+ else
262
+ write_inheritable_attribute(:attachment_definitions, {})
263
+ end
264
+ end
265
+
239
266
  attachment_definitions[name] = {:validations => []}.merge(options)
240
267
 
241
268
  after_save :save_attached_files
@@ -275,7 +302,7 @@ module Paperclip
275
302
  min = options[:greater_than] || (options[:in] && options[:in].first) || 0
276
303
  max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
277
304
  range = (min..max)
278
- message = options[:message] || "file size must be between :min and :max bytes."
305
+ message = options[:message] || "file size must be between :min and :max bytes"
279
306
  message = message.call if message.respond_to?(:call)
280
307
  message = message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
281
308
 
@@ -301,7 +328,7 @@ module Paperclip
301
328
  # be run is this lambda or method returns true.
302
329
  # * +unless+: Same as +if+ but validates if lambda or method returns false.
303
330
  def validates_attachment_presence name, options = {}
304
- message = options[:message] || "must be set."
331
+ message = options[:message] || "must be set"
305
332
  validates_presence_of :"#{name}_file_name",
306
333
  :message => message,
307
334
  :if => options[:if],
@@ -324,6 +351,8 @@ module Paperclip
324
351
  # NOTE: If you do not specify an [attachment]_content_type field on your
325
352
  # model, content_type validation will work _ONLY upon assignment_ and
326
353
  # re-validation after the instance has been reloaded will always succeed.
354
+ # You'll still need to have a virtual attribute (created by +attr_accessor+)
355
+ # name +[attachment]_content_type+ to be able to use this validator.
327
356
  def validates_attachment_content_type name, options = {}
328
357
  validation_options = options.dup
329
358
  allowed_types = [validation_options[:content_type]].flatten
@@ -343,7 +372,11 @@ module Paperclip
343
372
  # Returns the attachment definitions defined by each call to
344
373
  # has_attached_file.
345
374
  def attachment_definitions
346
- read_inheritable_attribute(:attachment_definitions)
375
+ if respond_to?(:class_attribute)
376
+ self.attachment_definitions
377
+ else
378
+ read_inheritable_attribute(:attachment_definitions)
379
+ end
347
380
  end
348
381
  end
349
382
 
@@ -1,17 +1,21 @@
1
- def obtain_class
2
- class_name = ENV['CLASS'] || ENV['class']
3
- raise "Must specify CLASS" unless class_name
4
- class_name
5
- end
1
+ module Paperclip
2
+ module Task
3
+ def self.obtain_class
4
+ class_name = ENV['CLASS'] || ENV['class']
5
+ raise "Must specify CLASS" unless class_name
6
+ class_name
7
+ end
6
8
 
7
- def obtain_attachments(klass)
8
- klass = Object.const_get(klass.to_s)
9
- name = ENV['ATTACHMENT'] || ENV['attachment']
10
- raise "Class #{klass.name} has no attachments specified" unless klass.respond_to?(:attachment_definitions)
11
- if !name.blank? && klass.attachment_definitions.keys.include?(name)
12
- [ name ]
13
- else
14
- klass.attachment_definitions.keys
9
+ def self.obtain_attachments(klass)
10
+ klass = Paperclip.class_for(klass.to_s)
11
+ name = ENV['ATTACHMENT'] || ENV['attachment']
12
+ raise "Class #{klass.name} has no attachments specified" unless klass.respond_to?(:attachment_definitions)
13
+ if !name.blank? && klass.attachment_definitions.keys.include?(name)
14
+ [ name ]
15
+ else
16
+ klass.attachment_definitions.keys
17
+ end
18
+ end
15
19
  end
16
20
  end
17
21
 
@@ -20,14 +24,15 @@ namespace :paperclip do
20
24
  task :refresh => ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"]
21
25
 
22
26
  namespace :refresh do
23
- desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT)."
27
+ desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT and STYLES splitted by comma)."
24
28
  task :thumbnails => :environment do
25
29
  errors = []
26
- klass = obtain_class
27
- names = obtain_attachments(klass)
30
+ klass = Paperclip::Task.obtain_class
31
+ names = Paperclip::Task.obtain_attachments(klass)
32
+ styles = (ENV['STYLES'] || ENV['styles'] || '').split(',').map(&:to_sym)
28
33
  names.each do |name|
29
34
  Paperclip.each_instance_with_attachment(klass, name) do |instance|
30
- result = instance.send(name).reprocess!
35
+ instance.send(name).reprocess!(*styles)
31
36
  errors << [instance.id, instance.errors] unless instance.errors.blank?
32
37
  end
33
38
  end
@@ -36,15 +41,19 @@ namespace :paperclip do
36
41
 
37
42
  desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
38
43
  task :metadata => :environment do
39
- klass = obtain_class
40
- names = obtain_attachments(klass)
44
+ klass = Paperclip::Task.obtain_class
45
+ names = Paperclip::Task.obtain_attachments(klass)
41
46
  names.each do |name|
42
47
  Paperclip.each_instance_with_attachment(klass, name) do |instance|
43
- if file = instance.send(name).to_file
48
+ if file = instance.send(name).to_file(:original)
44
49
  instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
45
50
  instance.send("#{name}_content_type=", file.content_type.strip)
46
51
  instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
47
- instance.save(false)
52
+ if Rails.version >= "3.0.0"
53
+ instance.save(:validate => false)
54
+ else
55
+ instance.save(false)
56
+ end
48
57
  else
49
58
  true
50
59
  end
@@ -55,8 +64,8 @@ namespace :paperclip do
55
64
 
56
65
  desc "Cleans out invalid attachments. Useful after you've added new validations."
57
66
  task :clean => :environment do
58
- klass = obtain_class
59
- names = obtain_attachments(klass)
67
+ klass = Paperclip::Task.obtain_class
68
+ names = Paperclip::Task.obtain_attachments(klass)
60
69
  names.each do |name|
61
70
  Paperclip.each_instance_with_attachment(klass, name) do |instance|
62
71
  instance.send(name).send(:validate)
@@ -14,6 +14,28 @@ class AttachmentTest < Test::Unit::TestCase
14
14
  assert_equal "#{Rails.root}/public/fake_models/1234/fake", @attachment.path
15
15
  end
16
16
 
17
+ should "return the url by interpolating the default_url option when no file assigned" do
18
+ @attachment = attachment :default_url => ":class/blegga.png"
19
+ @model = @attachment.instance
20
+ assert_nil @model.avatar_file_name
21
+ assert_equal "fake_models/blegga.png", @attachment.url
22
+ end
23
+
24
+ should "return the url by executing and interpolating the default_url Proc when no file assigned" do
25
+ @attachment = attachment :default_url => lambda { |a| ":class/blegga.png" }
26
+ @model = @attachment.instance
27
+ assert_nil @model.avatar_file_name
28
+ assert_equal "fake_models/blegga.png", @attachment.url
29
+ end
30
+
31
+ should "return the url by executing and interpolating the default_url Proc with attachment arg when no file assigned" do
32
+ @attachment = attachment :default_url => lambda { |a| a.instance.some_method_to_determine_default_url }
33
+ @model = @attachment.instance
34
+ @model.stubs(:some_method_to_determine_default_url).returns(":class/blegga.png")
35
+ assert_nil @model.avatar_file_name
36
+ assert_equal "fake_models/blegga.png", @attachment.url
37
+ end
38
+
17
39
  context "Attachment default_options" do
18
40
  setup do
19
41
  rebuild_model
@@ -76,6 +98,19 @@ class AttachmentTest < Test::Unit::TestCase
76
98
  end
77
99
  end
78
100
  end
101
+
102
+ context "with nested hash default" do
103
+ setup do
104
+ @nested_hash = {:thumb => {:first => "second" }}
105
+ Paperclip::Attachment.default_options[:styles] = @nested_hash
106
+ @dummy = Dummy.new
107
+ @attachment = @dummy.avatar
108
+ end
109
+
110
+ should "correctly clone the nested hash" do
111
+ assert_equal(@nested_hash, @attachment.instance_variable_get(:@styles))
112
+ end
113
+ end
79
114
  end
80
115
  end
81
116
 
@@ -228,6 +263,24 @@ class AttachmentTest < Test::Unit::TestCase
228
263
  end
229
264
  end
230
265
 
266
+ context "An attachment with :only_process" do
267
+ setup do
268
+ rebuild_model :styles => {
269
+ :thumb => "100x100",
270
+ :large => "400x400"
271
+ },
272
+ :only_process => [:thumb]
273
+ @file = StringIO.new("...")
274
+ @attachment = Dummy.new.avatar
275
+ end
276
+
277
+ should "only process the provided style" do
278
+ @attachment.expects(:post_process).with(:thumb)
279
+ @attachment.expects(:post_process).with(:large).never
280
+ @attachment.assign(@file)
281
+ end
282
+ end
283
+
231
284
  context "An attachment with :convert_options that is a proc" do
232
285
  setup do
233
286
  rebuild_model :styles => {
@@ -287,6 +340,24 @@ class AttachmentTest < Test::Unit::TestCase
287
340
  assert_equal "50x50#", @attachment.styles[:thumb][:geometry]
288
341
  end
289
342
  end
343
+
344
+ context "An attachment with conditional :styles that is a proc" do
345
+ setup do
346
+ rebuild_model :styles => lambda{ |attachment| attachment.instance.other == 'a' ? {:thumb => "50x50#"} : {:large => "400x400"} }
347
+
348
+ @dummy = Dummy.new(:other => 'a')
349
+ end
350
+
351
+ should "have the correct styles for the assigned instance values" do
352
+ assert_equal "50x50#", @dummy.avatar.styles[:thumb][:geometry]
353
+ assert_nil @dummy.avatar.styles[:large]
354
+
355
+ @dummy.other = 'b'
356
+
357
+ assert_equal "400x400", @dummy.avatar.styles[:large][:geometry]
358
+ assert_nil @dummy.avatar.styles[:thumb]
359
+ end
360
+ end
290
361
 
291
362
  context "An attachment with :url that is a proc" do
292
363
  setup do
@@ -425,6 +496,19 @@ class AttachmentTest < Test::Unit::TestCase
425
496
  rebuild_model :storage => :FileSystem
426
497
  @dummy = Dummy.new
427
498
  assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
499
+
500
+ rebuild_model :storage => :Filesystem
501
+ @dummy = Dummy.new
502
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
503
+ end
504
+
505
+ should "convert underscored storage name to camelcase" do
506
+ rebuild_model :storage => :not_here
507
+ @dummy = Dummy.new
508
+ exception = assert_raises(Paperclip::StorageMethodNotFound) do |e|
509
+ @dummy.avatar
510
+ end
511
+ assert exception.message.include?("NotHere")
428
512
  end
429
513
 
430
514
  should "raise an error if you try to include a storage module that doesn't exist" do
@@ -642,6 +726,15 @@ class AttachmentTest < Test::Unit::TestCase
642
726
  assert_equal nil, @attachment.path(:blah)
643
727
  end
644
728
 
729
+ context "with a file assigned but not saved yet" do
730
+ should "clear out any attached files" do
731
+ @attachment.assign(@file)
732
+ assert !@attachment.queued_for_write.blank?
733
+ @attachment.clear
734
+ assert @attachment.queued_for_write.blank?
735
+ end
736
+ end
737
+
645
738
  context "with a file assigned in the database" do
646
739
  setup do
647
740
  @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
@@ -918,4 +1011,27 @@ class AttachmentTest < Test::Unit::TestCase
918
1011
  end
919
1012
  end
920
1013
  end
1014
+
1015
+ context "an attachment with delete_file option set to false" do
1016
+ setup do
1017
+ rebuild_model :preserve_files => true
1018
+ @dummy = Dummy.new
1019
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
1020
+ @dummy.avatar = @file
1021
+ @dummy.save!
1022
+ @attachment = @dummy.avatar
1023
+ @path = @attachment.path
1024
+ end
1025
+
1026
+ should "not delete the files from storage when attachment is destroyed" do
1027
+ @attachment.destroy
1028
+ assert File.exists?(@path)
1029
+ end
1030
+
1031
+ should "not dleete the file when model is destroy" do
1032
+ @dummy.destroy
1033
+ assert File.exists?(@path)
1034
+ end
1035
+ end
1036
+
921
1037
  end
Binary file
data/test/fog_test.rb CHANGED
@@ -16,15 +16,20 @@ class FogTest < Test::Unit::TestCase
16
16
  }
17
17
 
18
18
  @connection = Fog::Storage.new(@credentials)
19
+ @connection.directories.create(
20
+ :key => @fog_directory
21
+ )
19
22
 
20
- rebuild_model(
23
+ @options = {
21
24
  :fog_directory => @fog_directory,
22
25
  :fog_credentials => @credentials,
23
26
  :fog_host => nil,
24
- :fog_public => true,
27
+ :fog_file => {:cache_control => 1234},
25
28
  :path => ":attachment/:basename.:extension",
26
29
  :storage => :fog
27
- )
30
+ }
31
+
32
+ rebuild_model(@options)
28
33
  end
29
34
 
30
35
  should "be extended by the Fog module" do
@@ -46,16 +51,17 @@ class FogTest < Test::Unit::TestCase
46
51
  end
47
52
 
48
53
  context "without a bucket" do
49
- should "succeed" do
54
+ setup do
55
+ @connection.directories.get(@fog_directory).destroy
56
+ end
57
+
58
+ should "create the bucket" do
50
59
  assert @dummy.save
60
+ assert @connection.directories.get(@fog_directory)
51
61
  end
52
62
  end
53
63
 
54
64
  context "with a bucket" do
55
- setup do
56
- @connection.directories.create(:key => @fog_directory)
57
- end
58
-
59
65
  should "succeed" do
60
66
  assert @dummy.save
61
67
  end
@@ -63,14 +69,7 @@ class FogTest < Test::Unit::TestCase
63
69
 
64
70
  context "without a fog_host" do
65
71
  setup do
66
- rebuild_model(
67
- :fog_directory => @fog_directory,
68
- :fog_credentials => @credentials,
69
- :fog_host => nil,
70
- :fog_public => true,
71
- :path => ":attachment/:basename.:extension",
72
- :storage => :fog
73
- )
72
+ rebuild_model(@options.merge(:fog_host => nil))
74
73
  @dummy = Dummy.new
75
74
  @dummy.avatar = StringIO.new('.')
76
75
  @dummy.save
@@ -82,12 +81,24 @@ class FogTest < Test::Unit::TestCase
82
81
  end
83
82
 
84
83
  context "with a fog_host" do
84
+ setup do
85
+ rebuild_model(@options.merge(:fog_host => 'http://example.com'))
86
+ @dummy = Dummy.new
87
+ @dummy.avatar = StringIO.new('.')
88
+ @dummy.save
89
+ end
90
+
91
+ should "provide a public url" do
92
+ assert @dummy.avatar.url =~ /^http:\/\/example\.com\/avatars\/stringio\.txt\?\d*$/
93
+ end
94
+ end
95
+
96
+ context "with a fog_host that includes a wildcard placeholder" do
85
97
  setup do
86
98
  rebuild_model(
87
99
  :fog_directory => @fog_directory,
88
100
  :fog_credentials => @credentials,
89
- :fog_host => 'http://example.com',
90
- :fog_public => true,
101
+ :fog_host => 'http://img%d.example.com',
91
102
  :path => ":attachment/:basename.:extension",
92
103
  :storage => :fog
93
104
  )
@@ -97,7 +108,7 @@ class FogTest < Test::Unit::TestCase
97
108
  end
98
109
 
99
110
  should "provide a public url" do
100
- assert @dummy.avatar.url =~ /^http:\/\/example\.com\/avatars\/stringio\.txt\?\d*$/
111
+ assert @dummy.avatar.url =~ /^http:\/\/img[0123]\.example\.com\/avatars\/stringio\.txt\?\d*$/
101
112
  end
102
113
  end
103
114