paperclip 3.0.2 → 3.0.3

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.

Files changed (47) hide show
  1. data/CONTRIBUTING.md +34 -2
  2. data/NEWS +33 -1
  3. data/README.md +20 -6
  4. data/RUNNING_TESTS.md +4 -0
  5. data/features/basic_integration.feature +4 -2
  6. data/features/support/fakeweb.rb +7 -0
  7. data/lib/paperclip.rb +2 -1
  8. data/lib/paperclip/attachment.rb +28 -16
  9. data/lib/paperclip/glue.rb +8 -0
  10. data/lib/paperclip/helpers.rb +4 -16
  11. data/lib/paperclip/instance_methods.rb +1 -1
  12. data/lib/paperclip/io_adapters/attachment_adapter.rb +13 -4
  13. data/lib/paperclip/io_adapters/file_adapter.rb +5 -3
  14. data/lib/paperclip/io_adapters/stringio_adapter.rb +4 -2
  15. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +3 -1
  16. data/lib/paperclip/missing_attachment_styles.rb +5 -8
  17. data/lib/paperclip/railtie.rb +4 -7
  18. data/lib/paperclip/storage/fog.rb +11 -0
  19. data/lib/paperclip/storage/s3.rb +26 -0
  20. data/lib/paperclip/tempfile.rb +7 -5
  21. data/lib/paperclip/validators.rb +1 -0
  22. data/lib/paperclip/validators/attachment_content_type_validator.rb +8 -1
  23. data/lib/paperclip/version.rb +1 -1
  24. data/lib/tasks/paperclip.rake +1 -1
  25. data/paperclip.gemspec +3 -3
  26. data/test/attachment_test.rb +77 -23
  27. data/test/geometry_test.rb +3 -3
  28. data/test/helper.rb +10 -10
  29. data/test/integration_test.rb +103 -56
  30. data/test/{attachment_adapter_test.rb → io_adapters/attachment_adapter_test.rb} +4 -1
  31. data/test/io_adapters/file_adapter_test.rb +88 -0
  32. data/test/{identity_adapter_test.rb → io_adapters/identity_adapter_test.rb} +0 -0
  33. data/test/{nil_adapter_test.rb → io_adapters/nil_adapter_test.rb} +0 -0
  34. data/test/{adapter_registry_test.rb → io_adapters/registry_test.rb} +0 -0
  35. data/test/{stringio_adapter_test.rb → io_adapters/stringio_adapter_test.rb} +0 -0
  36. data/test/{uploaded_file_adapter_test.rb → io_adapters/uploaded_file_adapter_test.rb} +0 -0
  37. data/test/paperclip_missing_attachment_styles_test.rb +10 -12
  38. data/test/paperclip_test.rb +4 -2
  39. data/test/storage/filesystem_test.rb +24 -16
  40. data/test/storage/fog_test.rb +14 -5
  41. data/test/storage/s3_live_test.rb +40 -12
  42. data/test/storage/s3_test.rb +13 -9
  43. data/test/style_test.rb +1 -1
  44. data/test/thumbnail_test.rb +7 -3
  45. data/test/validators/attachment_content_type_validator_test.rb +53 -1
  46. metadata +30 -28
  47. data/test/file_adapter_test.rb +0 -43
@@ -1,3 +1,6 @@
1
+ Contributing
2
+ ============
3
+
1
4
  We love pull requests. Here's a quick guide:
2
5
 
3
6
  1. Fork the repo.
@@ -13,7 +16,6 @@ a test!
13
16
 
14
17
  5. Push to your fork and submit a pull request.
15
18
 
16
-
17
19
  At this point you're waiting on us. We like to at least comment on, if not
18
20
  accept, pull requests within three business days (and, typically, one business
19
21
  day). We may suggest some changes or improvements or alternatives.
@@ -26,7 +28,37 @@ taken straight from the Ruby on Rails guide:
26
28
  * Update the documentation, the surrounding one, examples elsewhere, guides,
27
29
  whatever is affected by your contribution
28
30
 
29
- Syntax:
31
+ Running Tests
32
+ -------------
33
+
34
+ Paperclip uses [Appraisal](https://github.com/thoughtbot/appraisal) to aid
35
+ testing against multiple version of Ruby on Rails. This helps us to make sure
36
+ that Paperclip performs correctly with them.
37
+
38
+ ### Bootstrapping your test suite:
39
+
40
+ bundle install
41
+ bundle exec rake appraisal:install
42
+
43
+ This will install all the required gems that requires to test against each
44
+ version of Rails, which defined in `gemfiles/*.gemfile`.
45
+
46
+ ### To run a full test suite:
47
+
48
+ bundle exec rake
49
+
50
+ This will run Test::Unit and Cucumber against all version of Rails
51
+
52
+ ### To run single Test::Unit or Cucumber test
53
+
54
+ You need to specify a `BUNDLE_GEMFILE` pointing to the gemfile before running
55
+ the normal test command:
56
+
57
+ BUNDLE_GEMFILE=gemfiles/3.2.gemfile ruby -Itest test/schema_test.rb
58
+ BUNDLE_GEMFILE=gemfiles/3.2.gemfile cucumber features/basic_integration.feature
59
+
60
+ Syntax
61
+ ------
30
62
 
31
63
  * Two spaces, no tabs.
32
64
  * No trailing whitespace. Blank lines should not have any space.
data/NEWS CHANGED
@@ -1,4 +1,36 @@
1
- New in 3.0.1
1
+ New in 3.0.3:
2
+
3
+ * Bug fix: ThumbnailProcessor now correctly detects and preserve animated GIF.
4
+ * Bug fix: File extension is now preserved in generated Tempfile from adapter.
5
+ * Bug fix: Uploading file with unicode file name now won't raise an error when
6
+ logging in the AWS is turned on.
7
+ * Bug fix: Task "paperclip:refresh:missing_styles" now work correctly.
8
+ * Bug fix: Handle the case when :restricted_characters is nil.
9
+ * Bug fix: Don't delete all the existing styles if we reprocess.
10
+ * Bug fix: Content type is now ensured to not having a new line character.
11
+ * API CHANGE: Non-Rails usage should include Paperclip::Glue directly.
12
+
13
+ `Paperclip::Railtie` was intended to be used with Ruby on Rails only. If you're
14
+ using Paperclip without Rails, you should include `Paperclip::Glue` into
15
+ `ActiveRecord::Base` instead of requiring `paperclip/railtie`:
16
+
17
+ ActiveRecord::Base.send :include, Paperclip::Glue
18
+
19
+ * Bug fix: AttachmentContentTypeValidator now allow you to specify :allow_blank/:allow_nil
20
+ * Bug fix: Make sure content type always a String.
21
+ * Bug fix: Fix attachment.reprocess! when using storage providers fog and s3.
22
+ * Bug fix: Fix a problem with incorrect content_type detected with 'file' command for an empty file on Mac.
23
+
24
+ New in 3.0.2:
25
+
26
+ * API CHANGE: Generated migration class name is now plural (AddAttachmentToUsers instead of AddAttachmentToUser)
27
+ * API CHANGE: Remove Rails plugin initialization code.
28
+ * API CHANGE: Explicitly require Ruby 1.9.2 in the Gemfile.
29
+ * Bug fix: Fixes AWS::S3::Errors::RequestTimeout on Model#save.
30
+ * Bug fix: Fix a problem when there's no logger specified.
31
+ * Bug fix: Fix a problem when attaching Rack::Test::UploadedFile instance.
32
+
33
+ New in 3.0.1:
2
34
 
3
35
  * Feature: Introduce Paperlip IO adapter.
4
36
  * Bug fix: Regression in AttachmentContentTypeValidator has been fixed.
data/README.md CHANGED
@@ -183,6 +183,19 @@ Lastly, you can also define multiple validations on a single attachment using `v
183
183
  Storage
184
184
  -------
185
185
 
186
+ Paperclip ships with 3 storage adapters:
187
+
188
+ * File Storage
189
+ * S3 Storage (via `aws-sdk`)
190
+ * Fog Storage
191
+
192
+ If you would like to use Paperclip with another storage, you can install these
193
+ gems along side with Paperclip:
194
+
195
+ * [Windows Azure](https://github.com/gmontard/paperclip-azure-storage)
196
+
197
+ ### Understanding Storage
198
+
186
199
  The files that are assigned as attachments are, by default, placed in the
187
200
  directory specified by the `:path` option to `has_attached_file`. By default, this
188
201
  location is `:rails_root/public/system/:attachment/:id/:style/:filename`. This
@@ -191,7 +204,7 @@ location was chosen because on standard Capistrano deployments, the
191
204
  will survive between deployments. For example, using that `:path`, you may have a
192
205
  file at
193
206
 
194
- /data/myapp/releases/20081229172410/public/system/avatars/13/small/my_pic.png
207
+ /data/myapp/releases/20081229172410/public/system/user/avatar/000/000/013/small/my_pic.png
195
208
 
196
209
  _**NOTE**: This is a change from previous versions of Paperclip, but is overall a
197
210
  safer choice for the default file store._
@@ -240,7 +253,7 @@ geometry and a format, which the file will be converted to, like so:
240
253
  This will convert the "thumb" style to a 32x32 square in png format, regardless
241
254
  of what was uploaded. If the format is not specified, it is kept the same (i.e.
242
255
  jpgs will remain jpgs). For more information on the accepted style formats, see
243
- [http://www.imagemagick.org/script/command-line-processing.php#geometry](here).
256
+ [here](http://www.imagemagick.org/script/command-line-processing.php#geometry).
244
257
 
245
258
  Multiple processors can be specified, and they will be invoked in the order
246
259
  they are defined in the :processors array. Each successive processor will
@@ -451,13 +464,13 @@ If you'd like to contribute a feature or bugfix: Thanks! To make sure your
451
464
  fix/feature has a high chance of being included, please read the following
452
465
  guidelines:
453
466
 
454
- 1. Ask on the mailing list[http://groups.google.com/group/paperclip-plugin], or
455
- post a new GitHub Issue[http://github.com/thoughtbot/paperclip/issues].
467
+ 1. Ask on the [mailing list](http://groups.google.com/group/paperclip-plugin), or
468
+ post a new [GitHub Issue](http://github.com/thoughtbot/paperclip/issues).
456
469
  2. Make sure there are tests! We will not accept any patch that is not tested.
457
470
  It's a rare time when explicit tests aren't needed. If you have questions
458
471
  about writing tests for paperclip, please ask the mailing list.
459
472
 
460
- Please see CONTRIBUTING.md for details.
473
+ Please see `CONTRIBUTING.md` for more details on contributing and running test.
461
474
 
462
475
  Credits
463
476
  -------
@@ -473,4 +486,5 @@ The names and logos for thoughtbot are trademarks of thoughtbot, inc.
473
486
  License
474
487
  -------
475
488
 
476
- Paperclip is Copyright © 2008-2011 thoughtbot. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
489
+ Paperclip is Copyright © 2008-2011 thoughtbot. It is free software, and may be
490
+ redistributed under the terms specified in the MIT-LICENSE file.
@@ -0,0 +1,4 @@
1
+ Running Tests
2
+ =============
3
+
4
+ Please see `CONTRIBUTING.md` in "Running Tests" section for more information.
@@ -12,7 +12,8 @@ Feature: Rails integration
12
12
  Given I add this snippet to the User model:
13
13
  """
14
14
  attr_accessible :name, :attachment
15
- has_attached_file :attachment, :url => "/system/:attachment/:style/:filename"
15
+ has_attached_file :attachment, :url => "/system/:attachment/:style/:filename",
16
+ :styles => { :square => "100x100#" }
16
17
  """
17
18
  And I start the rails application
18
19
  When I go to the new user page
@@ -30,7 +31,8 @@ Feature: Rails integration
30
31
  has_attached_file :attachment,
31
32
  :storage => :s3,
32
33
  :path => "/:attachment/:style/:filename",
33
- :s3_credentials => Rails.root.join("config/s3.yml")
34
+ :s3_credentials => Rails.root.join("config/s3.yml"),
35
+ :styles => { :square => "100x100#" }
34
36
  """
35
37
  And I write to "config/s3.yml" with:
36
38
  """
@@ -1,3 +1,10 @@
1
1
  require 'fake_web'
2
2
 
3
3
  FakeWeb.allow_net_connect = false
4
+
5
+ module FakeWeb
6
+ class StubSocket
7
+ def read_timeout=(ignored)
8
+ end
9
+ end
10
+ end
@@ -46,11 +46,12 @@ require 'paperclip/validators'
46
46
  require 'paperclip/instance_methods'
47
47
  require 'paperclip/logger'
48
48
  require 'paperclip/helpers'
49
- require 'paperclip/railtie'
50
49
  require 'mime/types'
51
50
  require 'logger'
52
51
  require 'cocaine'
53
52
 
53
+ require 'paperclip/railtie' if defined?(Rails)
54
+
54
55
  # The base module that gets included in ActiveRecord::Base. See the
55
56
  # documentation for Paperclip::ClassMethods for more useful information.
56
57
  module Paperclip
@@ -90,12 +90,13 @@ module Paperclip
90
90
  ensure_required_accessors!
91
91
  file = Paperclip.io_adapters.for(uploaded_file)
92
92
 
93
- self.clear
93
+ @options[:only_process].map!(&:to_sym)
94
+ self.clear(*@options[:only_process])
94
95
  return nil if file.nil?
95
96
 
96
97
  @queued_for_write[:original] = file
97
98
  instance_write(:file_name, cleanup_filename(file.original_filename))
98
- instance_write(:content_type, file.content_type)
99
+ instance_write(:content_type, file.content_type.to_s.strip)
99
100
  instance_write(:file_size, file.size)
100
101
  instance_write(:fingerprint, file.fingerprint) if instance_respond_to?(:fingerprint)
101
102
  instance_write(:updated_at, Time.now)
@@ -160,11 +161,13 @@ module Paperclip
160
161
  end
161
162
 
162
163
  def styles
163
- styling_option = @options[:styles]
164
- if styling_option.respond_to?(:call) || !@normalized_styles
165
- @normalized_styles = ActiveSupport::OrderedHash.new
166
- (styling_option.respond_to?(:call) ? styling_option.call(self) : styling_option).each do |name, args|
167
- @normalized_styles[name] = Paperclip::Style.new(name, args.dup, self)
164
+ if @options[:styles].respond_to?(:call) || @normalized_styles.nil?
165
+ styles = @options[:styles]
166
+ styles = styles.call(self) if styles.respond_to?(:call)
167
+
168
+ @normalized_styles = styles.dup
169
+ @normalized_styles.each_pair do |name, options|
170
+ @normalized_styles[name.to_sym] = Paperclip::Style.new(name.to_sym, options.dup, self)
168
171
  end
169
172
  end
170
173
  @normalized_styles
@@ -202,10 +205,14 @@ module Paperclip
202
205
  # Clears out the attachment. Has the same effect as previously assigning
203
206
  # nil to the attachment. Does NOT save. If you wish to clear AND save,
204
207
  # use #destroy.
205
- def clear
206
- queue_existing_for_delete
207
- @queued_for_write = {}
208
- @errors = {}
208
+ def clear(*styles_to_clear)
209
+ if styles_to_clear.any?
210
+ queue_some_for_delete(*styles_to_clear)
211
+ else
212
+ queue_all_for_delete
213
+ @queued_for_write = {}
214
+ @errors = {}
215
+ end
209
216
  end
210
217
 
211
218
  # Destroys the attachment. Has the same effect as previously assigning
@@ -371,6 +378,7 @@ module Paperclip
371
378
 
372
379
  def post_process(*style_args) #:nodoc:
373
380
  return if @queued_for_write[:original].nil?
381
+
374
382
  instance.run_paperclip_callbacks(:post_process) do
375
383
  instance.run_paperclip_callbacks(:"#{name}_post_process") do
376
384
  post_process_styles(*style_args)
@@ -406,7 +414,13 @@ module Paperclip
406
414
  interpolator.interpolate(pattern, self, style_name)
407
415
  end
408
416
 
409
- def queue_existing_for_delete #:nodoc:
417
+ def queue_some_for_delete(*styles)
418
+ @queued_for_delete += styles.uniq.map do |style|
419
+ path(style) if exists?(style)
420
+ end.compact
421
+ end
422
+
423
+ def queue_all_for_delete #:nodoc:
410
424
  return if @options[:preserve_files] || !file?
411
425
  @queued_for_delete += [:original, *styles.keys].uniq.map do |style|
412
426
  path(style) if exists?(style)
@@ -426,15 +440,13 @@ module Paperclip
426
440
 
427
441
  # called by storage after the writes are flushed and before @queued_for_writes is cleared
428
442
  def after_flush_writes
429
- @queued_for_write.each do |style, file|
430
- # file.close unless file.closed?
431
- # file.unlink if file.respond_to?(:unlink) && file.path.present? && File.exist?(file.path)
432
- end
433
443
  end
434
444
 
435
445
  def cleanup_filename(filename)
436
446
  if @options[:restricted_characters]
437
447
  filename.gsub(@options[:restricted_characters], '_')
448
+ else
449
+ filename
438
450
  end
439
451
  end
440
452
  end
@@ -1,4 +1,6 @@
1
1
  require 'paperclip/callbacks'
2
+ require 'paperclip/validators'
3
+ require 'paperclip/schema'
2
4
 
3
5
  module Paperclip
4
6
  module Glue
@@ -8,6 +10,12 @@ module Paperclip
8
10
  base.send :include, Validators
9
11
  base.class_attribute :attachment_definitions
10
12
 
13
+ if defined?(ActiveRecord)
14
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, Paperclip::Schema)
15
+ ActiveRecord::ConnectionAdapters::Table.send(:include, Paperclip::Schema)
16
+ ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, Paperclip::Schema)
17
+ end
18
+
11
19
  locale_path = Dir.glob(File.dirname(__FILE__) + "/locales/*.{rb,yml}")
12
20
  I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
13
21
  end
@@ -32,26 +32,14 @@ module Paperclip
32
32
  # Find all instances of the given Active Record model +klass+ with attachment +name+.
33
33
  # This method is used by the refresh rake tasks.
34
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
35
+ class_for(klass).unscoped.where("#{name}_file_name IS NOT NULL").find_each do |instance|
36
+ yield(instance)
40
37
  end
41
38
  end
42
39
 
43
40
  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
41
+ class_name.split('::').inject(Object) do |klass, partial_class_name|
42
+ klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name, false) : klass.const_missing(partial_class_name)
55
43
  end
56
44
  end
57
45
 
@@ -28,7 +28,7 @@ module Paperclip
28
28
  def prepare_for_destroy
29
29
  Paperclip.log("Scheduling attachments for deletion.")
30
30
  each_attachment do |name, attachment|
31
- attachment.send(:queue_existing_for_delete)
31
+ attachment.send(:queue_all_for_delete)
32
32
  end
33
33
  end
34
34
  end
@@ -1,6 +1,5 @@
1
1
  module Paperclip
2
2
  class AttachmentAdapter
3
-
4
3
  def initialize(target)
5
4
  @target = target
6
5
  cache_current_values
@@ -30,6 +29,11 @@ module Paperclip
30
29
  @tempfile.read(length, buffer)
31
30
  end
32
31
 
32
+ # We don't use this directly, but aws/sdk does.
33
+ def rewind
34
+ @tempfile.rewind
35
+ end
36
+
33
37
  def eof?
34
38
  @tempfile.eof?
35
39
  end
@@ -48,12 +52,17 @@ module Paperclip
48
52
  end
49
53
 
50
54
  def copy_to_tempfile(src)
51
- dest = Tempfile.new(src.original_filename)
55
+ extension = File.extname(src.original_filename)
56
+ basename = File.basename(src.original_filename, extension)
57
+ dest = Tempfile.new([basename, extension])
52
58
  dest.binmode
53
- FileUtils.cp(src.path(:original), dest.path)
59
+ if src.respond_to? :copy_to_local_file
60
+ src.copy_to_local_file(:original, dest.path)
61
+ else
62
+ FileUtils.cp(src.path(:original), dest.path)
63
+ end
54
64
  dest
55
65
  end
56
-
57
66
  end
58
67
  end
59
68
 
@@ -56,20 +56,22 @@ module Paperclip
56
56
  private
57
57
 
58
58
  def copy_to_tempfile(src)
59
- dest = Tempfile.new(original_filename)
59
+ extension = File.extname(original_filename)
60
+ basename = File.basename(original_filename, extension)
61
+ dest = Tempfile.new([basename, extension])
60
62
  dest.binmode
61
63
  FileUtils.cp(src.path, dest.path)
62
64
  dest
63
65
  end
64
66
 
65
67
  def best_content_type_option(types)
66
- types.reject {|type| type.content_type.match(/\/x-/) }.first
68
+ types.reject {|type| type.content_type.match(/\/x-/) }.first.content_type
67
69
  end
68
70
 
69
71
  def type_from_file_command
70
72
  # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
71
73
  type = (self.original_filename.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
72
- mime_type = (Paperclip.run("file", "-b --mime :file", :file => self.path).split(/[:;]\s+/)[0] rescue "application/x-#{type}")
74
+ mime_type = (Paperclip.run("file", "-b --mime :file", :file => self.path).split(/[:;\s]+/)[0] rescue "application/x-#{type}")
73
75
  mime_type = "application/x-#{type}" if mime_type.match(/\(.*?\)/)
74
76
  mime_type
75
77
  end
@@ -16,7 +16,7 @@ module Paperclip
16
16
  def content_type
17
17
  @content_type ||= @target.content_type if @target.respond_to?(:content_type)
18
18
  @content_type ||= "text/plain"
19
- @content_type.strip
19
+ @content_type
20
20
  end
21
21
 
22
22
  def size
@@ -47,7 +47,9 @@ module Paperclip
47
47
  private
48
48
 
49
49
  def copy_to_tempfile(src)
50
- dest = Tempfile.new(original_filename)
50
+ extension = File.extname(original_filename)
51
+ basename = File.basename(original_filename, extension)
52
+ dest = Tempfile.new([basename, extension])
51
53
  dest.binmode
52
54
  while data = src.read(16*1024)
53
55
  dest.write(data)
@@ -50,7 +50,9 @@ module Paperclip
50
50
  private
51
51
 
52
52
  def copy_to_tempfile(src)
53
- dest = Tempfile.new(original_filename)
53
+ extension = File.extname(original_filename)
54
+ basename = File.basename(original_filename, extension)
55
+ dest = Tempfile.new([basename, extension])
54
56
  dest.binmode
55
57
  FileUtils.cp(src.path, dest.path)
56
58
  dest