paperclip 2.3.12 → 2.3.15

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/README.md CHANGED
@@ -188,9 +188,12 @@ or more or the processors, and they are expected to ignore it.
188
188
  _NOTE: Because processors operate by turning the original attachment into the
189
189
  styles, no processors will be run if there are no styles defined._
190
190
 
191
- If you're interested in caching your thumbnail's width, height and size in the
191
+ If you're interested in caching your thumbnail's width, height and size in the
192
192
  database, take a look at the [paperclip-meta](https://github.com/y8/paperclip-meta) gem.
193
193
 
194
+ Also, if you're interesting to generate the thumbnail on-the-fly, you might want
195
+ to look into the [attachment_on_the_fly](https://github.com/drpentode/Attachment-on-the-Fly) gem.
196
+
194
197
  Events
195
198
  ------
196
199
 
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require 'bundler/setup'
4
4
 
5
5
  require 'rake'
6
6
  require 'rake/testtask'
7
- require 'rake/rdoctask'
7
+ require 'rdoc/task'
8
8
 
9
9
  $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
10
10
  require 'paperclip'
@@ -31,7 +31,7 @@ task :shell do |t|
31
31
  end
32
32
 
33
33
  desc 'Generate documentation for the paperclip plugin.'
34
- Rake::RDocTask.new(:rdoc) do |rdoc|
34
+ RDoc::Task.new(:rdoc) do |rdoc|
35
35
  rdoc.rdoc_dir = 'doc'
36
36
  rdoc.title = 'Paperclip'
37
37
  rdoc.options << '--line-numbers' << '--inline-source'
@@ -1,7 +1,9 @@
1
1
  require 'rails/generators/active_record'
2
2
 
3
3
  class PaperclipGenerator < ActiveRecord::Generators::Base
4
- desc "Create a migration to add paperclip-specific fields to your model."
4
+ desc "Create a migration to add paperclip-specific fields to your model. " +
5
+ "The NAME argument is the name of your model, and the following " +
6
+ "arguments are the name of the attachments"
5
7
 
6
8
  argument :attachment_names, :required => true, :type => :array, :desc => "The names of the attachment(s) to add.",
7
9
  :banner => "attachment_one attachment_two attachment_three ..."
@@ -302,7 +302,7 @@ module Paperclip
302
302
  min = options[:greater_than] || (options[:in] && options[:in].first) || 0
303
303
  max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
304
304
  range = (min..max)
305
- 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"
306
306
  message = message.call if message.respond_to?(:call)
307
307
  message = message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
308
308
 
@@ -328,7 +328,7 @@ module Paperclip
328
328
  # be run is this lambda or method returns true.
329
329
  # * +unless+: Same as +if+ but validates if lambda or method returns false.
330
330
  def validates_attachment_presence name, options = {}
331
- message = options[:message] || "must be set."
331
+ message = options[:message] || "must be set"
332
332
  validates_presence_of :"#{name}_file_name",
333
333
  :message => message,
334
334
  :if => options[:if],
@@ -351,6 +351,8 @@ module Paperclip
351
351
  # NOTE: If you do not specify an [attachment]_content_type field on your
352
352
  # model, content_type validation will work _ONLY upon assignment_ and
353
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.
354
356
  def validates_attachment_content_type name, options = {}
355
357
  validation_options = options.dup
356
358
  allowed_types = [validation_options[:content_type]].flatten
@@ -21,7 +21,8 @@ module Paperclip
21
21
  :whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails],
22
22
  :use_default_time_zone => true,
23
23
  :hash_digest => "SHA1",
24
- :hash_data => ":class/:attachment/:id/:style/:updated_at"
24
+ :hash_data => ":class/:attachment/:id/:style/:updated_at",
25
+ :preserve_files => false
25
26
  }
26
27
  end
27
28
 
@@ -55,6 +56,7 @@ module Paperclip
55
56
  @hash_secret = options[:hash_secret]
56
57
  @convert_options = options[:convert_options]
57
58
  @processors = options[:processors]
59
+ @preserve_files = options[:preserve_files]
58
60
  @options = options
59
61
  @post_processing = true
60
62
  @queued_for_delete = []
@@ -67,7 +69,7 @@ module Paperclip
67
69
 
68
70
  def styles
69
71
  if @styles.respond_to?(:call) || !@normalized_styles
70
- @normalized_styles = {}
72
+ @normalized_styles = ActiveSupport::OrderedHash.new
71
73
  (@styles.respond_to?(:call) ? @styles.call(self) : @styles).each do |name, args|
72
74
  @normalized_styles[name] = Paperclip::Style.new(name, args.dup, self)
73
75
  end
@@ -167,6 +169,7 @@ module Paperclip
167
169
  # use #destroy.
168
170
  def clear
169
171
  queue_existing_for_delete
172
+ @queued_for_write = {}
170
173
  @errors = {}
171
174
  end
172
175
 
@@ -174,8 +177,10 @@ module Paperclip
174
177
  # nil to the attachment *and saving*. This is permanent. If you wish to
175
178
  # wipe out the existing attachment but not save, use #clear.
176
179
  def destroy
177
- clear
178
- save
180
+ unless @preserve_files
181
+ clear
182
+ save
183
+ end
179
184
  end
180
185
 
181
186
  # Returns the name of the file as originally assigned, and lives in the
@@ -225,9 +230,13 @@ module Paperclip
225
230
  end
226
231
 
227
232
  def generate_fingerprint(source)
228
- data = source.read
229
- source.rewind if source.respond_to?(:rewind)
230
- Digest::MD5.hexdigest(data)
233
+ if source.respond_to?(:path) && source.path && !source.path.blank?
234
+ Digest::MD5.file(source.path).to_s
235
+ else
236
+ data = source.read
237
+ source.rewind if source.respond_to?(:rewind)
238
+ Digest::MD5.hexdigest(data)
239
+ end
231
240
  end
232
241
 
233
242
  # Paths and URLs can have a number of variables interpolated into them
@@ -254,6 +263,7 @@ module Paperclip
254
263
  new_original.rewind
255
264
 
256
265
  @queued_for_write = { :original => new_original }
266
+ instance_write(:updated_at, Time.now)
257
267
  post_process(*style_args)
258
268
 
259
269
  old_original.close if old_original.respond_to?(:close)
@@ -271,7 +281,7 @@ module Paperclip
271
281
  def file?
272
282
  !original_filename.blank?
273
283
  end
274
-
284
+
275
285
  alias :present? :file?
276
286
 
277
287
  # Writes the attachment-specific attribute on the instance. For example,
@@ -361,7 +371,7 @@ module Paperclip
361
371
  end
362
372
 
363
373
  def queue_existing_for_delete #:nodoc:
364
- return unless file?
374
+ return unless (file? && @preserve_files==false)
365
375
  @queued_for_delete += [:original, *styles.keys].uniq.map do |style|
366
376
  path(style) if exists?(style)
367
377
  end.compact
@@ -30,7 +30,7 @@ module IOStream
30
30
  end
31
31
 
32
32
  # Corrects a bug in Windows when asking for Tempfile size.
33
- if defined? Tempfile
33
+ if defined?(Tempfile) && RUBY_PLATFORM !~ /java/
34
34
  class Tempfile
35
35
  def size
36
36
  if @tmpfile
@@ -38,8 +38,13 @@ module Paperclip
38
38
  file.close
39
39
  FileUtils.mkdir_p(File.dirname(path(style_name)))
40
40
  log("saving #{path(style_name)}")
41
- FileUtils.mv(file.path, path(style_name))
42
- FileUtils.chmod(0644, path(style_name))
41
+ begin
42
+ FileUtils.mv(file.path, path(style_name))
43
+ rescue SystemCallError
44
+ FileUtils.cp(file.path, path(style_name))
45
+ FileUtils.rm(file.path)
46
+ end
47
+ FileUtils.chmod(0666&~File.umask, path(style_name))
43
48
  end
44
49
  @queued_for_write = {}
45
50
  end
@@ -58,7 +63,7 @@ module Paperclip
58
63
  FileUtils.rmdir(path)
59
64
  break if File.exists?(path) # Ruby 1.9.2 does not raise if the removal failed.
60
65
  end
61
- rescue Errno::EEXIST, Errno::ENOTEMPTY, Errno::ENOENT, Errno::EINVAL, Errno::ENOTDIR
66
+ rescue Errno::EEXIST, Errno::ENOTEMPTY, Errno::ENOENT, Errno::EINVAL, Errno::ENOTDIR, Errno::EACCES
62
67
  # Stop trying to remove parent directories
63
68
  rescue SystemCallError => e
64
69
  log("There was an unexpected error while deleting directories: #{e.class}")
@@ -45,6 +45,7 @@ module Paperclip
45
45
  @fog_credentials = @options[:fog_credentials]
46
46
  @fog_host = @options[:fog_host]
47
47
  @fog_public = @options[:fog_public]
48
+ @fog_file = @options[:fog_file] || {}
48
49
 
49
50
  @url = ':fog_public_url'
50
51
  Paperclip.interpolates(:fog_public_url) do |attachment, style|
@@ -64,11 +65,19 @@ module Paperclip
64
65
  def flush_writes
65
66
  for style, file in @queued_for_write do
66
67
  log("saving #{path(style)}")
67
- directory.files.create(
68
- :body => file,
69
- :key => path(style),
70
- :public => @fog_public
71
- )
68
+ retried = false
69
+ begin
70
+ directory.files.create(@fog_file.merge(
71
+ :body => file,
72
+ :key => path(style),
73
+ :public => @fog_public
74
+ ))
75
+ rescue Excon::Errors::NotFound
76
+ raise if retried
77
+ retried = true
78
+ directory.save
79
+ retry
80
+ end
72
81
  end
73
82
  @queued_for_write = {}
74
83
  end
@@ -115,12 +124,7 @@ module Paperclip
115
124
  end
116
125
 
117
126
  def directory
118
- @directory ||= begin
119
- connection.directories.get(@fog_directory) || connection.directories.create(
120
- :key => @fog_directory,
121
- :public => @fog_public
122
- )
123
- end
127
+ @directory ||= connection.directories.new(:key => @fog_directory)
124
128
  end
125
129
 
126
130
  end
@@ -27,6 +27,14 @@ module Paperclip
27
27
  # policies that S3 provides (more information can be found here:
28
28
  # http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAccessPolicy.html)
29
29
  # The default for Paperclip is :public_read.
30
+ #
31
+ # You can set permission on a per style bases by doing the following:
32
+ # :s3_permissions => {
33
+ # :original => :private
34
+ # }
35
+ # Or globaly:
36
+ # :s3_permissions => :private
37
+ #
30
38
  # * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either
31
39
  # 'http' or 'https'. Defaults to 'http' when your :s3_permissions are :public_read (the
32
40
  # default), and 'https' when your :s3_permissions are anything else.
@@ -72,14 +80,17 @@ module Paperclip
72
80
  @bucket = @options[:bucket] || @s3_credentials[:bucket]
73
81
  @bucket = @bucket.call(self) if @bucket.is_a?(Proc)
74
82
  @s3_options = @options[:s3_options] || {}
75
- @s3_permissions = @options[:s3_permissions] || :public_read
76
- @s3_protocol = @options[:s3_protocol] || (@s3_permissions == :public_read ? 'http' : 'https')
83
+ @s3_permissions = set_permissions(@options[:s3_permissions])
84
+ @s3_protocol = @options[:s3_protocol] ||
85
+ Proc.new do |style|
86
+ (@s3_permissions[style.to_sym] || @s3_permissions[:default]) == :public_read ? 'http' : 'https'
87
+ end
77
88
  @s3_headers = @options[:s3_headers] || {}
78
89
  @s3_host_alias = @options[:s3_host_alias]
79
90
  @s3_host_alias = @s3_host_alias.call(self) if @s3_host_alias.is_a?(Proc)
80
91
  unless @url.to_s.match(/^:s3.*url$/)
81
- @path = @path.gsub(/:url/, @url)
82
- @url = ":s3_path_url"
92
+ @path = @path.gsub(/:url/, @url)
93
+ @url = ":s3_path_url"
83
94
  end
84
95
  @url = ":asset_host" if @options[:url].to_s == ":asset_host"
85
96
  AWS::S3::Base.establish_connection!( @s3_options.merge(
@@ -88,13 +99,13 @@ module Paperclip
88
99
  ))
89
100
  end
90
101
  Paperclip.interpolates(:s3_alias_url) do |attachment, style|
91
- "#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
102
+ "#{attachment.s3_protocol(style)}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
92
103
  end unless Paperclip::Interpolations.respond_to? :s3_alias_url
93
104
  Paperclip.interpolates(:s3_path_url) do |attachment, style|
94
- "#{attachment.s3_protocol}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
105
+ "#{attachment.s3_protocol(style)}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
95
106
  end unless Paperclip::Interpolations.respond_to? :s3_path_url
96
107
  Paperclip.interpolates(:s3_domain_url) do |attachment, style|
97
- "#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}"
108
+ "#{attachment.s3_protocol(style)}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}"
98
109
  end unless Paperclip::Interpolations.respond_to? :s3_domain_url
99
110
  Paperclip.interpolates(:asset_host) do |attachment, style|
100
111
  "#{attachment.path(style).gsub(%r{^/}, "")}"
@@ -102,20 +113,30 @@ module Paperclip
102
113
  end
103
114
 
104
115
  def expiring_url(time = 3600, style_name = default_style)
105
- AWS::S3::S3Object.url_for(path(style_name), bucket_name, :expires_in => time, :use_ssl => (s3_protocol == 'https'))
116
+ AWS::S3::S3Object.url_for(path(style_name), bucket_name, :expires_in => time, :use_ssl => (s3_protocol(style_name) == 'https'))
106
117
  end
107
118
 
108
119
  def bucket_name
109
120
  @bucket
110
121
  end
111
122
 
123
+ def set_permissions permissions
124
+ if permissions.is_a?(Hash)
125
+ permissions[:default] = permissions[:default] || :public_read
126
+ else
127
+ permissions = { :default => permissions || :public_read }
128
+ end
129
+ permissions
130
+ end
131
+
112
132
  def s3_host_alias
113
133
  @s3_host_alias
114
134
  end
115
135
 
116
136
  def parse_credentials creds
117
137
  creds = find_credentials(creds).stringify_keys
118
- (creds[Rails.env] || creds).symbolize_keys
138
+ env = Object.const_defined?(:Rails) ? Rails.env : nil
139
+ (creds[env] || creds).symbolize_keys
119
140
  end
120
141
 
121
142
  def exists?(style = default_style)
@@ -126,8 +147,12 @@ module Paperclip
126
147
  end
127
148
  end
128
149
 
129
- def s3_protocol
130
- @s3_protocol
150
+ def s3_protocol(style)
151
+ if @s3_protocol.is_a?(Proc)
152
+ @s3_protocol.call(style)
153
+ else
154
+ @s3_protocol
155
+ end
131
156
  end
132
157
 
133
158
  # Returns representation of the data of the file assigned to the given
@@ -156,7 +181,7 @@ module Paperclip
156
181
  file,
157
182
  bucket_name,
158
183
  {:content_type => file.content_type.to_s.strip,
159
- :access => @s3_permissions,
184
+ :access => (@s3_permissions[style] || @s3_permissions[:default]),
160
185
  }.merge(@s3_headers))
161
186
  rescue AWS::S3::NoSuchBucket => e
162
187
  create_bucket
@@ -76,6 +76,7 @@ module Paperclip
76
76
  def transformation_command
77
77
  scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
78
78
  trans = []
79
+ trans << "-coalesce" if animated?
79
80
  trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
80
81
  trans << "-crop" << %["#{crop}"] << "+repage" if crop
81
82
  trans
@@ -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,24 +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 %r"svg" then "image/svg+xml"
15
- when "txt" then "text/plain"
16
- when %r"html?" then "text/html"
17
- when "js" then "application/js"
18
- 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
19
16
  else
20
- # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
21
- content_type = (Paperclip.run("file", "-b --mime-type :file", :file => self.path).split(':').last.strip rescue "application/x-#{type}")
22
- content_type = "application/x-#{type}" if content_type.match(/\(.*?\)/)
23
- content_type
17
+ iterate_over_array_to_find_best_option(types)
24
18
  end
25
19
  end
26
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
+
27
33
  # Returns the file's normal name.
28
34
  def original_filename
29
35
  File.basename(self.path)
@@ -1,3 +1,3 @@
1
1
  module Paperclip
2
- VERSION = "2.3.12" unless defined? Paperclip::VERSION
2
+ VERSION = "2.3.15" unless defined? Paperclip::VERSION
3
3
  end
@@ -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 = Paperclip.class_for(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
 
@@ -23,8 +27,8 @@ namespace :paperclip do
23
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)
28
32
  styles = (ENV['STYLES'] || ENV['styles'] || '').split(',').map(&:to_sym)
29
33
  names.each do |name|
30
34
  Paperclip.each_instance_with_attachment(klass, name) do |instance|
@@ -37,11 +41,11 @@ namespace :paperclip do
37
41
 
38
42
  desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
39
43
  task :metadata => :environment do
40
- klass = obtain_class
41
- names = obtain_attachments(klass)
44
+ klass = Paperclip::Task.obtain_class
45
+ names = Paperclip::Task.obtain_attachments(klass)
42
46
  names.each do |name|
43
47
  Paperclip.each_instance_with_attachment(klass, name) do |instance|
44
- if file = instance.send(name).to_file
48
+ if file = instance.send(name).to_file(:original)
45
49
  instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
46
50
  instance.send("#{name}_content_type=", file.content_type.strip)
47
51
  instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
@@ -60,8 +64,8 @@ namespace :paperclip do
60
64
 
61
65
  desc "Cleans out invalid attachments. Useful after you've added new validations."
62
66
  task :clean => :environment do
63
- klass = obtain_class
64
- names = obtain_attachments(klass)
67
+ klass = Paperclip::Task.obtain_class
68
+ names = Paperclip::Task.obtain_attachments(klass)
65
69
  names.each do |name|
66
70
  Paperclip.each_instance_with_attachment(klass, name) do |instance|
67
71
  instance.send(name).send(:validate)
@@ -0,0 +1,2 @@
1
+ require 'paperclip/railtie'
2
+ Paperclip::Railtie.insert
@@ -98,6 +98,19 @@ class AttachmentTest < Test::Unit::TestCase
98
98
  end
99
99
  end
100
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
101
114
  end
102
115
  end
103
116
 
@@ -621,6 +634,46 @@ class AttachmentTest < Test::Unit::TestCase
621
634
  assert_equal "sheep_say_bæ.png", @dummy.avatar.original_filename
622
635
  end
623
636
  end
637
+
638
+ context "Attachment with uppercase extension and a default style" do
639
+ setup do
640
+ @old_defaults = Paperclip::Attachment.default_options.dup
641
+ Paperclip::Attachment.default_options.merge!({
642
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
643
+ })
644
+ FileUtils.rm_rf("tmp")
645
+ rebuild_model
646
+ @instance = Dummy.new
647
+ @instance.stubs(:id).returns 123
648
+
649
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "uppercase.PNG"), 'rb')
650
+
651
+ styles = {:styles => { :large => ["400x400", :jpg],
652
+ :medium => ["100x100", :jpg],
653
+ :small => ["32x32#", :jpg]},
654
+ :default_style => :small}
655
+ @attachment = Paperclip::Attachment.new(:avatar,
656
+ @instance,
657
+ styles)
658
+ now = Time.now
659
+ Time.stubs(:now).returns(now)
660
+ @attachment.assign(@file)
661
+ @attachment.save
662
+ end
663
+
664
+ teardown do
665
+ @file.close
666
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
667
+ end
668
+
669
+ should "should have matching to_s and url methods" do
670
+ file = @attachment.to_file
671
+ assert file
672
+ assert_match @attachment.to_s, @attachment.url
673
+ assert_match @attachment.to_s(:small), @attachment.url(:small)
674
+ file.close
675
+ end
676
+ end
624
677
 
625
678
  context "Attachment with uppercase extension and a default style" do
626
679
  setup do
@@ -700,6 +753,15 @@ class AttachmentTest < Test::Unit::TestCase
700
753
  assert_equal nil, @attachment.path(:blah)
701
754
  end
702
755
 
756
+ context "with a file assigned but not saved yet" do
757
+ should "clear out any attached files" do
758
+ @attachment.assign(@file)
759
+ assert !@attachment.queued_for_write.blank?
760
+ @attachment.clear
761
+ assert @attachment.queued_for_write.blank?
762
+ end
763
+ end
764
+
703
765
  context "with a file assigned in the database" do
704
766
  setup do
705
767
  @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
@@ -976,4 +1038,27 @@ class AttachmentTest < Test::Unit::TestCase
976
1038
  end
977
1039
  end
978
1040
  end
1041
+
1042
+ context "an attachment with delete_file option set to false" do
1043
+ setup do
1044
+ rebuild_model :preserve_files => true
1045
+ @dummy = Dummy.new
1046
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
1047
+ @dummy.avatar = @file
1048
+ @dummy.save!
1049
+ @attachment = @dummy.avatar
1050
+ @path = @attachment.path
1051
+ end
1052
+
1053
+ should "not delete the files from storage when attachment is destroyed" do
1054
+ @attachment.destroy
1055
+ assert File.exists?(@path)
1056
+ end
1057
+
1058
+ should "not dleete the file when model is destroy" do
1059
+ @dummy.destroy
1060
+ assert File.exists?(@path)
1061
+ end
1062
+ end
1063
+
979
1064
  end
@@ -16,15 +16,21 @@ 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
27
  :fog_public => true,
28
+ :fog_file => {:cache_control => 1234},
25
29
  :path => ":attachment/:basename.:extension",
26
30
  :storage => :fog
27
- )
31
+ }
32
+
33
+ rebuild_model(@options)
28
34
  end
29
35
 
30
36
  should "be extended by the Fog module" do
@@ -46,16 +52,17 @@ class FogTest < Test::Unit::TestCase
46
52
  end
47
53
 
48
54
  context "without a bucket" do
49
- should "succeed" do
55
+ setup do
56
+ @connection.directories.get(@fog_directory).destroy
57
+ end
58
+
59
+ should "create the bucket" do
50
60
  assert @dummy.save
61
+ assert @connection.directories.get(@fog_directory)
51
62
  end
52
63
  end
53
64
 
54
65
  context "with a bucket" do
55
- setup do
56
- @connection.directories.create(:key => @fog_directory)
57
- end
58
-
59
66
  should "succeed" do
60
67
  assert @dummy.save
61
68
  end
@@ -63,14 +70,7 @@ class FogTest < Test::Unit::TestCase
63
70
 
64
71
  context "without a fog_host" do
65
72
  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
- )
73
+ rebuild_model(@options.merge(:fog_host => nil))
74
74
  @dummy = Dummy.new
75
75
  @dummy.avatar = StringIO.new('.')
76
76
  @dummy.save
@@ -83,14 +83,7 @@ class FogTest < Test::Unit::TestCase
83
83
 
84
84
  context "with a fog_host" do
85
85
  setup do
86
- rebuild_model(
87
- :fog_directory => @fog_directory,
88
- :fog_credentials => @credentials,
89
- :fog_host => 'http://example.com',
90
- :fog_public => true,
91
- :path => ":attachment/:basename.:extension",
92
- :storage => :fog
93
- )
86
+ rebuild_model(@options.merge(:fog_host => 'http://example.com'))
94
87
  @dummy = Dummy.new
95
88
  @dummy.avatar = StringIO.new('.')
96
89
  @dummy.save
@@ -8,6 +8,7 @@ require 'mocha'
8
8
  require 'active_record'
9
9
  require 'active_record/version'
10
10
  require 'active_support'
11
+ require 'mime/types'
11
12
 
12
13
  puts "Testing against version #{ActiveRecord::VERSION::STRING}"
13
14
 
@@ -58,6 +58,7 @@ class IntegrationTest < Test::Unit::TestCase
58
58
  has_attached_file :avatar, :styles => { :thumb => "150x25#", :dynamic => lambda { |a| '50x50#' } }
59
59
  end
60
60
  @d2 = Dummy.find(@dummy.id)
61
+ @original_timestamp = @d2.avatar_updated_at
61
62
  @d2.avatar.reprocess!
62
63
  @d2.save
63
64
  end
@@ -66,6 +67,10 @@ class IntegrationTest < Test::Unit::TestCase
66
67
  assert_match /\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
67
68
  assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"`
68
69
  end
70
+
71
+ should "change the timestamp" do
72
+ assert_not_equal @original_timestamp, @d2.avatar_updated_at
73
+ end
69
74
  end
70
75
  end
71
76
 
@@ -383,6 +388,24 @@ class IntegrationTest < Test::Unit::TestCase
383
388
  assert_equal "5k.png", @dummy.avatar_file_name
384
389
  end
385
390
 
391
+ [000,002,022].each do |umask|
392
+ context "when the umask is #{umask}" do
393
+ setup do
394
+ @umask = File.umask umask
395
+ end
396
+
397
+ teardown do
398
+ File.umask @umask
399
+ end
400
+
401
+ should "respect the current umask" do
402
+ @dummy.avatar = @file
403
+ @dummy.save
404
+ assert_equal 0666&~umask, 0666&File.stat(@dummy.avatar.path).mode
405
+ end
406
+ end
407
+ end
408
+
386
409
  context "that is assigned its file from another Paperclip attachment" do
387
410
  setup do
388
411
  @dummy2 = Dummy.new
@@ -216,6 +216,7 @@ class StorageTest < Test::Unit::TestCase
216
216
  :production => { :bucket => "prod_bucket" },
217
217
  :development => { :bucket => "dev_bucket" }
218
218
  },
219
+ :s3_permissions => :private,
219
220
  :s3_host_alias => "something.something.com",
220
221
  :path => ":attachment/:style/:basename.:extension",
221
222
  :url => ":s3_alias_url"
@@ -225,10 +226,10 @@ class StorageTest < Test::Unit::TestCase
225
226
  @dummy = Dummy.new
226
227
  @dummy.avatar = StringIO.new(".")
227
228
 
228
- AWS::S3::S3Object.expects(:url_for).with("avatars/original/stringio.txt", "prod_bucket", { :expires_in => 3600, :use_ssl => false })
229
+ AWS::S3::S3Object.expects(:url_for).with("avatars/original/stringio.txt", "prod_bucket", { :expires_in => 3600, :use_ssl => true })
229
230
  @dummy.avatar.expiring_url
230
231
 
231
- AWS::S3::S3Object.expects(:url_for).with("avatars/thumb/stringio.txt", "prod_bucket", { :expires_in => 1800, :use_ssl => false })
232
+ AWS::S3::S3Object.expects(:url_for).with("avatars/thumb/stringio.txt", "prod_bucket", { :expires_in => 1800, :use_ssl => true })
232
233
  @dummy.avatar.expiring_url(1800, :thumb)
233
234
  end
234
235
 
@@ -432,6 +433,132 @@ class StorageTest < Test::Unit::TestCase
432
433
  assert_equal 'env_secret', AWS::S3::Base.connection.options[:secret_access_key]
433
434
  end
434
435
  end
436
+
437
+ context "S3 Permissions" do
438
+ context "defaults to public-read" do
439
+ setup do
440
+ rebuild_model :storage => :s3,
441
+ :bucket => "testing",
442
+ :path => ":attachment/:style/:basename.:extension",
443
+ :s3_credentials => {
444
+ 'access_key_id' => "12345",
445
+ 'secret_access_key' => "54321"
446
+ }
447
+ end
448
+
449
+ context "when assigned" do
450
+ setup do
451
+ @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
452
+ @dummy = Dummy.new
453
+ @dummy.avatar = @file
454
+ end
455
+
456
+ teardown { @file.close }
457
+
458
+ context "and saved" do
459
+ setup do
460
+ AWS::S3::Base.stubs(:establish_connection!)
461
+ AWS::S3::S3Object.expects(:store).with(@dummy.avatar.path,
462
+ anything,
463
+ 'testing',
464
+ :content_type => 'image/png',
465
+ :access => :public_read)
466
+ @dummy.save
467
+ end
468
+
469
+ should "succeed" do
470
+ assert true
471
+ end
472
+ end
473
+ end
474
+ end
475
+
476
+ context "string permissions set" do
477
+ setup do
478
+ rebuild_model :storage => :s3,
479
+ :bucket => "testing",
480
+ :path => ":attachment/:style/:basename.:extension",
481
+ :s3_credentials => {
482
+ 'access_key_id' => "12345",
483
+ 'secret_access_key' => "54321"
484
+ },
485
+ :s3_permissions => 'private'
486
+ end
487
+
488
+ context "when assigned" do
489
+ setup do
490
+ @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
491
+ @dummy = Dummy.new
492
+ @dummy.avatar = @file
493
+ end
494
+
495
+ teardown { @file.close }
496
+
497
+ context "and saved" do
498
+ setup do
499
+ AWS::S3::Base.stubs(:establish_connection!)
500
+ AWS::S3::S3Object.expects(:store).with(@dummy.avatar.path,
501
+ anything,
502
+ 'testing',
503
+ :content_type => 'image/png',
504
+ :access => 'private')
505
+ @dummy.save
506
+ end
507
+
508
+ should "succeed" do
509
+ assert true
510
+ end
511
+ end
512
+ end
513
+ end
514
+
515
+ context "hash permissions set" do
516
+ setup do
517
+ rebuild_model :storage => :s3,
518
+ :bucket => "testing",
519
+ :path => ":attachment/:style/:basename.:extension",
520
+ :styles => {
521
+ :thumb => "80x80>"
522
+ },
523
+ :s3_credentials => {
524
+ 'access_key_id' => "12345",
525
+ 'secret_access_key' => "54321"
526
+ },
527
+ :s3_permissions => {
528
+ :original => 'private',
529
+ :thumb => 'public-read'
530
+ }
531
+ end
532
+
533
+ context "when assigned" do
534
+ setup do
535
+ @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
536
+ @dummy = Dummy.new
537
+ @dummy.avatar = @file
538
+ end
539
+
540
+ teardown { @file.close }
541
+
542
+ context "and saved" do
543
+ setup do
544
+ AWS::S3::Base.stubs(:establish_connection!)
545
+ [:thumb, :original].each do |style|
546
+ AWS::S3::S3Object.expects(:store).with("avatars/#{style}/5k.png",
547
+ anything,
548
+ 'testing',
549
+ :content_type => 'image/png',
550
+ :access => style == :thumb ? 'public-read' : 'private')
551
+ end
552
+ @dummy.save
553
+ end
554
+
555
+ should "succeed" do
556
+ assert true
557
+ end
558
+ end
559
+ end
560
+ end
561
+ end
435
562
 
436
563
  unless ENV["S3_TEST_BUCKET"].blank?
437
564
  context "Using S3 for real, an attachment with S3 storage" do
@@ -62,12 +62,12 @@ class StyleTest < Test::Unit::TestCase
62
62
 
63
63
  context "An attachment with style rules in various forms" do
64
64
  setup do
65
+ styles = ActiveSupport::OrderedHash.new
66
+ styles[:aslist] = ["100x100", :png]
67
+ styles[:ashash] = {:geometry => "100x100", :format => :png}
68
+ styles[:asstring] = "100x100"
65
69
  @attachment = attachment :path => ":basename.:extension",
66
- :styles => {
67
- :aslist => ["100x100", :png],
68
- :ashash => {:geometry => "100x100", :format => :png},
69
- :asstring => "100x100"
70
- }
70
+ :styles => styles
71
71
  end
72
72
  should "have the right number of styles" do
73
73
  assert_kind_of Hash, @attachment.styles
@@ -92,6 +92,9 @@ class StyleTest < Test::Unit::TestCase
92
92
  assert_nil @attachment.styles[:asstring].format
93
93
  end
94
94
 
95
+ should "retain order" do
96
+ assert_equal [:aslist, :ashash, :asstring], @attachment.styles.keys
97
+ end
95
98
  end
96
99
 
97
100
  context "An attachment with :convert_options" do
@@ -138,7 +141,7 @@ class StyleTest < Test::Unit::TestCase
138
141
  end
139
142
 
140
143
  end
141
-
144
+
142
145
  context "A style rule with :processors supplied as procs" do
143
146
  setup do
144
147
  @attachment = attachment :path => ":basename.:extension",
@@ -289,6 +289,10 @@ class ThumbnailTest < Test::Unit::TestCase
289
289
  cmd = %Q[identify -format "%wx%h" "#{dst.path}"]
290
290
  assert_equal "50x50"*12, `#{cmd}`.chomp
291
291
  end
292
+
293
+ should "use the -coalesce option" do
294
+ assert_equal @thumb.transformation_command.first, "-coalesce"
295
+ end
292
296
  end
293
297
 
294
298
  context "with omitted output format" do
@@ -301,6 +305,10 @@ class ThumbnailTest < Test::Unit::TestCase
301
305
  cmd = %Q[identify -format "%wx%h" "#{dst.path}"]
302
306
  assert_equal "50x50"*12, `#{cmd}`.chomp
303
307
  end
308
+
309
+ should "use the -coalesce option" do
310
+ assert_equal @thumb.transformation_command.first, "-coalesce"
311
+ end
304
312
  end
305
313
  end
306
314
  end
@@ -10,9 +10,9 @@ class UpfileTest < Test::Unit::TestCase
10
10
  %w(txt) => 'text/plain',
11
11
  %w(htm html) => 'text/html',
12
12
  %w(csv) => 'text/csv',
13
- %w(xml) => 'text/xml',
13
+ %w(xml) => 'application/xml',
14
14
  %w(css) => 'text/css',
15
- %w(js) => 'application/js',
15
+ %w(js) => 'application/javascript',
16
16
  %w(foo) => 'application/x-foo'
17
17
  }.each do |extensions, content_type|
18
18
  extensions.each do |extension|
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paperclip
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
5
4
  prerelease:
6
- segments:
7
- - 2
8
- - 3
9
- - 12
10
- version: 2.3.12
5
+ version: 2.3.15
11
6
  platform: ruby
12
7
  authors:
13
8
  - Jon Yurek
@@ -15,127 +10,108 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-06-28 00:00:00 -04:00
13
+ date: 2011-07-10 00:00:00 -04:00
19
14
  default_executable:
20
15
  dependencies:
21
16
  - !ruby/object:Gem::Dependency
22
17
  name: activerecord
23
- prerelease: false
24
18
  requirement: &id001 !ruby/object:Gem::Requirement
25
19
  none: false
26
20
  requirements:
27
21
  - - ">="
28
22
  - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 2
32
- - 3
33
- - 0
34
23
  version: 2.3.0
35
24
  type: :runtime
25
+ prerelease: false
36
26
  version_requirements: *id001
37
27
  - !ruby/object:Gem::Dependency
38
28
  name: activesupport
39
- prerelease: false
40
29
  requirement: &id002 !ruby/object:Gem::Requirement
41
30
  none: false
42
31
  requirements:
43
32
  - - ">="
44
33
  - !ruby/object:Gem::Version
45
- hash: 7
46
- segments:
47
- - 2
48
- - 3
49
- - 2
50
34
  version: 2.3.2
51
35
  type: :runtime
36
+ prerelease: false
52
37
  version_requirements: *id002
53
38
  - !ruby/object:Gem::Dependency
54
39
  name: cocaine
55
- prerelease: false
56
40
  requirement: &id003 !ruby/object:Gem::Requirement
57
41
  none: false
58
42
  requirements:
59
43
  - - ">="
60
44
  - !ruby/object:Gem::Version
61
- hash: 27
62
- segments:
63
- - 0
64
- - 0
65
- - 2
66
45
  version: 0.0.2
67
46
  type: :runtime
47
+ prerelease: false
68
48
  version_requirements: *id003
69
49
  - !ruby/object:Gem::Dependency
70
- name: shoulda
71
- prerelease: false
50
+ name: mime-types
72
51
  requirement: &id004 !ruby/object:Gem::Requirement
73
52
  none: false
74
53
  requirements:
75
54
  - - ">="
76
55
  - !ruby/object:Gem::Version
77
- hash: 3
78
- segments:
79
- - 0
80
56
  version: "0"
81
- type: :development
57
+ type: :runtime
58
+ prerelease: false
82
59
  version_requirements: *id004
83
60
  - !ruby/object:Gem::Dependency
84
- name: appraisal
85
- prerelease: false
61
+ name: shoulda
86
62
  requirement: &id005 !ruby/object:Gem::Requirement
87
63
  none: false
88
64
  requirements:
89
65
  - - ">="
90
66
  - !ruby/object:Gem::Version
91
- hash: 3
92
- segments:
93
- - 0
94
67
  version: "0"
95
68
  type: :development
69
+ prerelease: false
96
70
  version_requirements: *id005
97
71
  - !ruby/object:Gem::Dependency
98
- name: mocha
99
- prerelease: false
72
+ name: appraisal
100
73
  requirement: &id006 !ruby/object:Gem::Requirement
101
74
  none: false
102
75
  requirements:
103
76
  - - ">="
104
77
  - !ruby/object:Gem::Version
105
- hash: 3
106
- segments:
107
- - 0
108
78
  version: "0"
109
79
  type: :development
80
+ prerelease: false
110
81
  version_requirements: *id006
111
82
  - !ruby/object:Gem::Dependency
112
- name: aws-s3
113
- prerelease: false
83
+ name: mocha
114
84
  requirement: &id007 !ruby/object:Gem::Requirement
115
85
  none: false
116
86
  requirements:
117
87
  - - ">="
118
88
  - !ruby/object:Gem::Version
119
- hash: 3
120
- segments:
121
- - 0
122
89
  version: "0"
123
90
  type: :development
91
+ prerelease: false
124
92
  version_requirements: *id007
125
93
  - !ruby/object:Gem::Dependency
126
- name: sqlite3-ruby
127
- prerelease: false
94
+ name: aws-s3
128
95
  requirement: &id008 !ruby/object:Gem::Requirement
129
96
  none: false
130
97
  requirements:
131
98
  - - ">="
132
99
  - !ruby/object:Gem::Version
133
- hash: 3
134
- segments:
135
- - 0
136
100
  version: "0"
137
101
  type: :development
102
+ prerelease: false
138
103
  version_requirements: *id008
104
+ - !ruby/object:Gem::Dependency
105
+ name: sqlite3-ruby
106
+ requirement: &id009 !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: "0"
112
+ type: :development
113
+ prerelease: false
114
+ version_requirements: *id009
139
115
  description: Easy upload management for ActiveRecord
140
116
  email: jyurek@thoughtbot.com
141
117
  executables: []
@@ -149,45 +125,33 @@ files:
149
125
  - LICENSE
150
126
  - Rakefile
151
127
  - init.rb
152
- - lib/paperclip.rb
128
+ - lib/generators/paperclip/paperclip_generator.rb
129
+ - lib/generators/paperclip/templates/paperclip_migration.rb.erb
130
+ - lib/generators/paperclip/USAGE
153
131
  - lib/paperclip/attachment.rb
154
132
  - lib/paperclip/callback_compatibility.rb
155
133
  - lib/paperclip/geometry.rb
156
134
  - lib/paperclip/interpolations.rb
157
135
  - lib/paperclip/iostream.rb
136
+ - lib/paperclip/matchers/have_attached_file_matcher.rb
137
+ - lib/paperclip/matchers/validate_attachment_content_type_matcher.rb
138
+ - lib/paperclip/matchers/validate_attachment_presence_matcher.rb
139
+ - lib/paperclip/matchers/validate_attachment_size_matcher.rb
158
140
  - lib/paperclip/matchers.rb
159
141
  - lib/paperclip/processor.rb
160
142
  - lib/paperclip/railtie.rb
143
+ - lib/paperclip/storage/filesystem.rb
144
+ - lib/paperclip/storage/fog.rb
145
+ - lib/paperclip/storage/s3.rb
161
146
  - lib/paperclip/storage.rb
162
147
  - lib/paperclip/style.rb
163
148
  - lib/paperclip/thumbnail.rb
164
149
  - lib/paperclip/upfile.rb
165
150
  - lib/paperclip/version.rb
151
+ - lib/paperclip.rb
166
152
  - lib/tasks/paperclip.rake
167
- - lib/paperclip/matchers/have_attached_file_matcher.rb
168
- - lib/paperclip/matchers/validate_attachment_content_type_matcher.rb
169
- - lib/paperclip/matchers/validate_attachment_presence_matcher.rb
170
- - lib/paperclip/matchers/validate_attachment_size_matcher.rb
171
- - lib/paperclip/storage/filesystem.rb
172
- - lib/paperclip/storage/fog.rb
173
- - lib/paperclip/storage/s3.rb
174
- - lib/generators/paperclip/paperclip_generator.rb
175
- - lib/generators/paperclip/USAGE
176
- - lib/generators/paperclip/templates/paperclip_migration.rb.erb
177
153
  - test/attachment_test.rb
178
154
  - test/database.yml
179
- - test/fog_test.rb
180
- - test/geometry_test.rb
181
- - test/helper.rb
182
- - test/integration_test.rb
183
- - test/interpolations_test.rb
184
- - test/iostream_test.rb
185
- - test/paperclip_test.rb
186
- - test/processor_test.rb
187
- - test/storage_test.rb
188
- - test/style_test.rb
189
- - test/thumbnail_test.rb
190
- - test/upfile_test.rb
191
155
  - test/fixtures/12k.png
192
156
  - test/fixtures/50x50.png
193
157
  - test/fixtures/5k.png
@@ -197,13 +161,26 @@ files:
197
161
  - test/fixtures/text.txt
198
162
  - test/fixtures/twopage.pdf
199
163
  - test/fixtures/uppercase.PNG
164
+ - test/fog_test.rb
165
+ - test/geometry_test.rb
166
+ - test/helper.rb
167
+ - test/integration_test.rb
168
+ - test/interpolations_test.rb
169
+ - test/iostream_test.rb
200
170
  - test/matchers/have_attached_file_matcher_test.rb
201
171
  - test/matchers/validate_attachment_content_type_matcher_test.rb
202
172
  - test/matchers/validate_attachment_presence_matcher_test.rb
203
173
  - test/matchers/validate_attachment_size_matcher_test.rb
174
+ - test/paperclip_test.rb
175
+ - test/processor_test.rb
176
+ - test/storage_test.rb
177
+ - test/style_test.rb
178
+ - test/thumbnail_test.rb
179
+ - test/upfile_test.rb
180
+ - rails/init.rb
204
181
  - generators/paperclip/paperclip_generator.rb
205
- - generators/paperclip/USAGE
206
182
  - generators/paperclip/templates/paperclip_migration.rb.erb
183
+ - generators/paperclip/USAGE
207
184
  - shoulda_macros/paperclip.rb
208
185
  has_rdoc: true
209
186
  homepage: https://github.com/thoughtbot/paperclip
@@ -220,7 +197,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
220
197
  requirements:
221
198
  - - ">="
222
199
  - !ruby/object:Gem::Version
223
- hash: 3
200
+ hash: 2036980330891834332
224
201
  segments:
225
202
  - 0
226
203
  version: "0"
@@ -229,14 +206,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
229
206
  requirements:
230
207
  - - ">="
231
208
  - !ruby/object:Gem::Version
232
- hash: 3
233
- segments:
234
- - 0
235
209
  version: "0"
236
210
  requirements:
237
211
  - ImageMagick
238
212
  rubyforge_project: paperclip
239
- rubygems_version: 1.5.2
213
+ rubygems_version: 1.6.2
240
214
  signing_key:
241
215
  specification_version: 3
242
216
  summary: File attachments as attributes for ActiveRecord