paperclip 4.2.2 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +17 -0
  3. data/.github/issue_template.md +3 -0
  4. data/.hound.yml +1055 -0
  5. data/.rubocop.yml +1 -0
  6. data/.travis.yml +17 -15
  7. data/Appraisals +4 -16
  8. data/CONTRIBUTING.md +19 -8
  9. data/Gemfile +5 -9
  10. data/LICENSE +1 -1
  11. data/MIGRATING-ES.md +317 -0
  12. data/MIGRATING.md +375 -0
  13. data/NEWS +184 -31
  14. data/README.md +371 -201
  15. data/RELEASING.md +17 -0
  16. data/Rakefile +2 -2
  17. data/UPGRADING +12 -9
  18. data/features/basic_integration.feature +10 -6
  19. data/features/migration.feature +0 -24
  20. data/features/step_definitions/attachment_steps.rb +41 -35
  21. data/features/step_definitions/html_steps.rb +2 -2
  22. data/features/step_definitions/rails_steps.rb +39 -38
  23. data/features/step_definitions/s3_steps.rb +2 -2
  24. data/features/step_definitions/web_steps.rb +1 -103
  25. data/features/support/env.rb +1 -0
  26. data/features/support/file_helpers.rb +2 -2
  27. data/features/support/paths.rb +1 -1
  28. data/features/support/rails.rb +0 -24
  29. data/gemfiles/4.2.gemfile +6 -8
  30. data/gemfiles/5.0.gemfile +17 -0
  31. data/lib/generators/paperclip/paperclip_generator.rb +9 -1
  32. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +1 -1
  33. data/lib/paperclip/attachment.rb +51 -26
  34. data/lib/paperclip/attachment_registry.rb +3 -2
  35. data/lib/paperclip/callbacks.rb +8 -6
  36. data/lib/paperclip/content_type_detector.rb +27 -11
  37. data/lib/paperclip/errors.rb +3 -1
  38. data/lib/paperclip/file_command_content_type_detector.rb +6 -8
  39. data/lib/paperclip/filename_cleaner.rb +0 -1
  40. data/lib/paperclip/geometry_detector_factory.rb +3 -3
  41. data/lib/paperclip/geometry_parser_factory.rb +1 -1
  42. data/lib/paperclip/glue.rb +1 -1
  43. data/lib/paperclip/has_attached_file.rb +9 -2
  44. data/lib/paperclip/helpers.rb +15 -11
  45. data/lib/paperclip/interpolations/plural_cache.rb +6 -5
  46. data/lib/paperclip/interpolations.rb +24 -14
  47. data/lib/paperclip/io_adapters/abstract_adapter.rb +32 -4
  48. data/lib/paperclip/io_adapters/attachment_adapter.rb +17 -6
  49. data/lib/paperclip/io_adapters/data_uri_adapter.rb +8 -8
  50. data/lib/paperclip/io_adapters/empty_string_adapter.rb +5 -4
  51. data/lib/paperclip/io_adapters/file_adapter.rb +12 -6
  52. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +8 -7
  53. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -6
  54. data/lib/paperclip/io_adapters/nil_adapter.rb +8 -5
  55. data/lib/paperclip/io_adapters/registry.rb +6 -2
  56. data/lib/paperclip/io_adapters/stringio_adapter.rb +9 -6
  57. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +10 -6
  58. data/lib/paperclip/io_adapters/uri_adapter.rb +43 -19
  59. data/lib/paperclip/logger.rb +1 -1
  60. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
  61. data/lib/paperclip/media_type_spoof_detector.rb +13 -9
  62. data/lib/paperclip/processor.rb +15 -6
  63. data/lib/paperclip/rails_environment.rb +25 -0
  64. data/lib/paperclip/schema.rb +4 -10
  65. data/lib/paperclip/storage/filesystem.rb +13 -2
  66. data/lib/paperclip/storage/fog.rb +33 -20
  67. data/lib/paperclip/storage/s3.rb +89 -70
  68. data/lib/paperclip/style.rb +0 -1
  69. data/lib/paperclip/thumbnail.rb +24 -12
  70. data/lib/paperclip/url_generator.rb +17 -13
  71. data/lib/paperclip/validators/attachment_size_validator.rb +1 -7
  72. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +4 -0
  73. data/lib/paperclip/validators.rb +1 -1
  74. data/lib/paperclip/version.rb +3 -1
  75. data/lib/paperclip.rb +27 -13
  76. data/lib/tasks/paperclip.rake +33 -3
  77. data/paperclip.gemspec +18 -15
  78. data/spec/paperclip/attachment_definitions_spec.rb +1 -1
  79. data/spec/paperclip/attachment_processing_spec.rb +2 -5
  80. data/spec/paperclip/attachment_registry_spec.rb +84 -13
  81. data/spec/paperclip/attachment_spec.rb +147 -41
  82. data/spec/paperclip/content_type_detector_spec.rb +9 -2
  83. data/spec/paperclip/file_command_content_type_detector_spec.rb +15 -2
  84. data/spec/paperclip/filename_cleaner_spec.rb +0 -1
  85. data/spec/paperclip/geometry_spec.rb +1 -1
  86. data/spec/paperclip/glue_spec.rb +44 -0
  87. data/spec/paperclip/has_attached_file_spec.rb +24 -8
  88. data/spec/paperclip/integration_spec.rb +42 -5
  89. data/spec/paperclip/interpolations_spec.rb +21 -9
  90. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +106 -23
  91. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +6 -3
  92. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +7 -1
  93. data/spec/paperclip/io_adapters/file_adapter_spec.rb +6 -3
  94. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +51 -14
  95. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +1 -1
  96. data/spec/paperclip/io_adapters/registry_spec.rb +2 -2
  97. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +5 -1
  98. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +5 -5
  99. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +126 -8
  100. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +10 -0
  101. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +1 -1
  102. data/spec/paperclip/media_type_spoof_detector_spec.rb +75 -11
  103. data/spec/paperclip/paperclip_spec.rb +15 -40
  104. data/spec/paperclip/plural_cache_spec.rb +17 -16
  105. data/spec/paperclip/processor_spec.rb +4 -4
  106. data/spec/paperclip/rails_environment_spec.rb +33 -0
  107. data/spec/paperclip/schema_spec.rb +46 -46
  108. data/spec/paperclip/storage/fog_spec.rb +63 -3
  109. data/spec/paperclip/storage/s3_live_spec.rb +20 -14
  110. data/spec/paperclip/storage/s3_spec.rb +400 -215
  111. data/spec/paperclip/style_spec.rb +0 -1
  112. data/spec/paperclip/tempfile_factory_spec.rb +4 -0
  113. data/spec/paperclip/tempfile_spec.rb +35 -0
  114. data/spec/paperclip/thumbnail_spec.rb +59 -38
  115. data/spec/paperclip/url_generator_spec.rb +55 -45
  116. data/spec/paperclip/validators/attachment_size_validator_spec.rb +26 -20
  117. data/spec/paperclip/validators_spec.rb +5 -5
  118. data/spec/spec_helper.rb +7 -1
  119. data/spec/support/assertions.rb +12 -1
  120. data/spec/support/fake_model.rb +4 -0
  121. data/spec/support/fixtures/empty.xlsx +0 -0
  122. data/spec/support/matchers/have_column.rb +11 -2
  123. data/spec/support/mock_attachment.rb +2 -0
  124. data/spec/support/mock_url_generator_builder.rb +2 -2
  125. data/spec/support/model_reconstruction.rb +11 -3
  126. data/spec/support/reporting.rb +11 -0
  127. metadata +110 -63
  128. data/RUNNING_TESTS.md +0 -4
  129. data/cucumber/paperclip_steps.rb +0 -6
  130. data/gemfiles/3.2.gemfile +0 -19
  131. data/gemfiles/4.0.gemfile +0 -19
  132. data/gemfiles/4.1.gemfile +0 -19
  133. data/lib/paperclip/locales/de.yml +0 -18
  134. data/lib/paperclip/locales/es.yml +0 -18
  135. data/lib/paperclip/locales/ja.yml +0 -18
  136. data/lib/paperclip/locales/pt-BR.yml +0 -18
  137. data/lib/paperclip/locales/zh-CN.yml +0 -18
  138. data/lib/paperclip/locales/zh-HK.yml +0 -18
  139. data/lib/paperclip/locales/zh-TW.yml +0 -18
  140. data/spec/support/mock_model.rb +0 -2
  141. data/spec/support/rails_helpers.rb +0 -7
@@ -3,10 +3,11 @@ module Paperclip
3
3
  class Thumbnail < Processor
4
4
 
5
5
  attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options,
6
- :source_file_options, :animated, :auto_orient
6
+ :source_file_options, :animated, :auto_orient, :frame_index
7
7
 
8
8
  # List of formats that we need to preserve animation
9
9
  ANIMATED_FORMATS = %w(gif)
10
+ MULTI_FRAME_FORMATS = %w(.mkv .avi .mp4 .mov .mpg .mpeg .gif)
10
11
 
11
12
  # Creates a Thumbnail object set to work on the +file+ given. It
12
13
  # will attempt to transform the image into one defined by +target_geometry+
@@ -25,11 +26,11 @@ module Paperclip
25
26
  # +whiny+ - whether to raise an error when processing fails. Defaults to true
26
27
  # +format+ - the desired filename extension
27
28
  # +animated+ - whether to merge all the layers in the image. Defaults to true
29
+ # +frame_index+ - the frame index of the source file to render as the thumbnail
28
30
  def initialize(file, options = {}, attachment = nil)
29
31
  super
30
32
 
31
33
  geometry = options[:geometry].to_s
32
- @file = file
33
34
  @crop = geometry[-1,1] == '#'
34
35
  @target_geometry = options.fetch(:string_geometry_parser, Geometry).parse(geometry)
35
36
  @current_geometry = options.fetch(:file_geometry_parser, Geometry).from_file(@file)
@@ -42,12 +43,12 @@ module Paperclip
42
43
  if @auto_orient && @current_geometry.respond_to?(:auto_orient)
43
44
  @current_geometry.auto_orient
44
45
  end
45
-
46
46
  @source_file_options = @source_file_options.split(/\s+/) if @source_file_options.respond_to?(:split)
47
47
  @convert_options = @convert_options.split(/\s+/) if @convert_options.respond_to?(:split)
48
48
 
49
49
  @current_format = File.extname(@file.path)
50
50
  @basename = File.basename(@file.path, @current_format)
51
+ @frame_index = multi_frame_format? ? options.fetch(:frame_index, 0) : 0
51
52
  end
52
53
 
53
54
  # Returns true if the +target_geometry+ is meant to crop.
@@ -64,8 +65,8 @@ module Paperclip
64
65
  # that contains the new image.
65
66
  def make
66
67
  src = @file
67
- dst = Tempfile.new([@basename, @format ? ".#{@format}" : ''])
68
- dst.binmode
68
+ filename = [@basename, @format ? ".#{@format}" : ""].join
69
+ dst = TempfileFactory.new.generate(filename)
69
70
 
70
71
  begin
71
72
  parameters = []
@@ -77,10 +78,18 @@ module Paperclip
77
78
 
78
79
  parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")
79
80
 
80
- success = convert(parameters, :source => "#{File.expand_path(src.path)}#{'[0]' unless animated?}", :dest => File.expand_path(dst.path))
81
- rescue Cocaine::ExitStatusError => e
82
- raise Paperclip::Error, "There was an error processing the thumbnail for #{@basename}" if @whiny
83
- rescue Cocaine::CommandNotFoundError => e
81
+ frame = animated? ? "" : "[#{@frame_index}]"
82
+ convert(
83
+ parameters,
84
+ source: "#{File.expand_path(src.path)}#{frame}",
85
+ dest: File.expand_path(dst.path),
86
+ )
87
+ rescue Terrapin::ExitStatusError => e
88
+ if @whiny
89
+ message = "There was an error processing the thumbnail for #{@basename}:\n" + e.message
90
+ raise Paperclip::Error, message
91
+ end
92
+ rescue Terrapin::CommandNotFoundError => e
84
93
  raise Paperclip::Errors::CommandNotFoundError.new("Could not run the `convert` command. Please install ImageMagick.")
85
94
  end
86
95
 
@@ -102,7 +111,10 @@ module Paperclip
102
111
 
103
112
  protected
104
113
 
105
- # Return true if the format is animated
114
+ def multi_frame_format?
115
+ MULTI_FRAME_FORMATS.include? @current_format
116
+ end
117
+
106
118
  def animated?
107
119
  @animated && (ANIMATED_FORMATS.include?(@format.to_s) || @format.blank?) && identified_as_animated?
108
120
  end
@@ -113,9 +125,9 @@ module Paperclip
113
125
  @identified_as_animated = ANIMATED_FORMATS.include? identify("-format %m :file", :file => "#{@file.path}[0]").to_s.downcase.strip
114
126
  end
115
127
  @identified_as_animated
116
- rescue Cocaine::ExitStatusError => e
128
+ rescue Terrapin::ExitStatusError => e
117
129
  raise Paperclip::Error, "There was an error running `identify` for #{@basename}" if @whiny
118
- rescue Cocaine::CommandNotFoundError => e
130
+ rescue Terrapin::CommandNotFoundError => e
119
131
  raise Paperclip::Errors::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
120
132
  end
121
133
  end
@@ -1,30 +1,34 @@
1
1
  require 'uri'
2
+ require 'active_support/core_ext/module/delegation'
2
3
 
3
4
  module Paperclip
4
5
  class UrlGenerator
5
- def initialize(attachment, attachment_options)
6
+ def initialize(attachment)
6
7
  @attachment = attachment
7
- @attachment_options = attachment_options
8
8
  end
9
9
 
10
10
  def for(style_name, options)
11
- timestamp_as_needed(
12
- escape_url_as_needed(
13
- @attachment_options[:interpolator].interpolate(most_appropriate_url, @attachment, style_name),
14
- options
15
- ), options)
11
+ interpolated = attachment_options[:interpolator].interpolate(
12
+ most_appropriate_url, @attachment, style_name
13
+ )
14
+
15
+ escaped = escape_url_as_needed(interpolated, options)
16
+ timestamp_as_needed(escaped, options)
16
17
  end
17
18
 
18
19
  private
19
20
 
21
+ attr_reader :attachment
22
+ delegate :options, to: :attachment, prefix: true
23
+
20
24
  # This method is all over the place.
21
25
  def default_url
22
- if @attachment_options[:default_url].respond_to?(:call)
23
- @attachment_options[:default_url].call(@attachment)
24
- elsif @attachment_options[:default_url].is_a?(Symbol)
25
- @attachment.instance.send(@attachment_options[:default_url])
26
+ if attachment_options[:default_url].respond_to?(:call)
27
+ attachment_options[:default_url].call(@attachment)
28
+ elsif attachment_options[:default_url].is_a?(Symbol)
29
+ @attachment.instance.send(attachment_options[:default_url])
26
30
  else
27
- @attachment_options[:default_url]
31
+ attachment_options[:default_url]
28
32
  end
29
33
  end
30
34
 
@@ -32,7 +36,7 @@ module Paperclip
32
36
  if @attachment.original_filename.nil?
33
37
  default_url
34
38
  else
35
- @attachment_options[:url]
39
+ attachment_options[:url]
36
40
  end
37
41
  end
38
42
 
@@ -71,13 +71,7 @@ module Paperclip
71
71
  end
72
72
 
73
73
  def human_size(size)
74
- if defined?(ActiveSupport::NumberHelper) # Rails 4.0+
75
- ActiveSupport::NumberHelper.number_to_human_size(size)
76
- else
77
- storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
78
- unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => size.to_i, :raise => true)
79
- storage_units_format.gsub(/%n/, size.to_i.to_s).gsub(/%u/, unit).html_safe
80
- end
74
+ ActiveSupport::NumberHelper.number_to_human_size(size)
81
75
  end
82
76
 
83
77
  def min_value_in_human_size(record)
@@ -8,6 +8,10 @@ module Paperclip
8
8
  if Paperclip::MediaTypeSpoofDetector.using(adapter, value.original_filename, value.content_type).spoofed?
9
9
  record.errors.add(attribute, :spoofed_media_type)
10
10
  end
11
+
12
+ if adapter.tempfile
13
+ adapter.tempfile.close(true)
14
+ end
11
15
  end
12
16
  end
13
17
 
@@ -36,7 +36,7 @@ module Paperclip
36
36
  options = attributes.extract_options!.dup
37
37
 
38
38
  Paperclip::Validators.constants.each do |constant|
39
- if constant.to_s =~ /\AAttachment(.+)Validator\Z/
39
+ if constant.to_s =~ /\AAttachment(.+)Validator\z/
40
40
  validator_kind = $1.underscore.to_sym
41
41
 
42
42
  if options.has_key?(validator_kind)
@@ -1,3 +1,5 @@
1
1
  module Paperclip
2
- VERSION = "4.2.2" unless defined? Paperclip::VERSION
2
+ unless defined?(Paperclip::VERSION)
3
+ VERSION = "6.1.0".freeze
4
+ end
3
5
  end
data/lib/paperclip.rb CHANGED
@@ -55,11 +55,21 @@ require 'paperclip/helpers'
55
55
  require 'paperclip/has_attached_file'
56
56
  require 'paperclip/attachment_registry'
57
57
  require 'paperclip/filename_cleaner'
58
- require 'mime/types'
58
+ require 'paperclip/rails_environment'
59
+
60
+ begin
61
+ # Use mime/types/columnar if available, for reduced memory usage
62
+ require "mime/types/columnar"
63
+ rescue LoadError
64
+ require "mime/types"
65
+ end
66
+
67
+ require 'mimemagic'
68
+ require 'mimemagic/overlay'
59
69
  require 'logger'
60
- require 'cocaine'
70
+ require 'terrapin'
61
71
 
62
- require 'paperclip/railtie' if defined?(Rails)
72
+ require 'paperclip/railtie' if defined?(Rails::Railtie)
63
73
 
64
74
  # The base module that gets included in ActiveRecord::Base. See the
65
75
  # documentation for Paperclip::ClassMethods for more useful information.
@@ -80,14 +90,15 @@ module Paperclip
80
90
  # image's orientation. Defaults to true.
81
91
  def self.options
82
92
  @options ||= {
83
- :whiny => true,
84
- :image_magick_path => nil,
85
- :command_path => nil,
86
- :log => true,
87
- :log_command => true,
88
- :swallow_stderr => true,
89
- :content_type_mappings => {},
90
- :use_exif_orientation => true
93
+ command_path: nil,
94
+ content_type_mappings: {},
95
+ log: true,
96
+ log_command: true,
97
+ read_timeout: nil,
98
+ swallow_stderr: true,
99
+ use_exif_orientation: true,
100
+ whiny: true,
101
+ is_windows: Gem.win_platform?
91
102
  }
92
103
  end
93
104
 
@@ -109,7 +120,7 @@ module Paperclip
109
120
  # called on it, the attachment will *not* be deleted until +save+ is called. See the
110
121
  # Paperclip::Attachment documentation for more specifics. There are a number of options
111
122
  # you can set to change the behavior of a Paperclip attachment:
112
- # * +url+: The full URL of where the attachment is publically accessible. This can just
123
+ # * +url+: The full URL of where the attachment is publicly accessible. This can just
113
124
  # as easily point to a directory served directly through Apache as it can to an action
114
125
  # that can control permissions. You can specify the full domain and path, but usually
115
126
  # just an absolute path is sufficient. The leading slash *must* be included manually for
@@ -118,6 +129,9 @@ module Paperclip
118
129
  # Paperclip::Attachment#interpolate for more information on variable interpolaton.
119
130
  # :url => "/:class/:attachment/:id/:style_:filename"
120
131
  # :url => "http://some.other.host/stuff/:class/:id_:extension"
132
+ # Note: When using the +s3+ storage option, the +url+ option expects
133
+ # particular values. See the Paperclip::Storage::S3#url documentation for
134
+ # specifics.
121
135
  # * +default_url+: The URL that will be returned if there is no attachment assigned.
122
136
  # This field is interpolated just as the url is. The default value is
123
137
  # "/:attachment/:style/missing.png"
@@ -136,7 +150,7 @@ module Paperclip
136
150
  # user.avatar.url # => "/avatars/23/normal_me.png"
137
151
  # * +keep_old_files+: Keep the existing attachment files (original + resized) from
138
152
  # being automatically deleted when an attachment is cleared or updated. Defaults to +false+.
139
- # * +preserve_files+: Keep the existing attachment files in all cases, even if the parent
153
+ # * +preserve_files+: Keep the existing attachment files in all cases, even if the parent
140
154
  # record is destroyed. Defaults to +false+.
141
155
  # * +whiny+: Will raise an error if Paperclip cannot post_process an uploaded file due
142
156
  # to a command line error. This will override the global setting for this attachment.
@@ -18,7 +18,7 @@ module Paperclip
18
18
  raise "Class #{klass.name} has no attachments specified"
19
19
  end
20
20
 
21
- if !name.blank? && attachment_names.map(&:to_s).include?(name.to_s)
21
+ if name.present? && attachment_names.map(&:to_s).include?(name.to_s)
22
22
  [ name ]
23
23
  else
24
24
  attachment_names
@@ -46,7 +46,7 @@ namespace :paperclip do
46
46
  attachment = instance.send(name)
47
47
  begin
48
48
  attachment.reprocess!(*styles)
49
- rescue Exception => e
49
+ rescue StandardError => e
50
50
  Paperclip::Task.log_error("exception while processing #{klass} ID #{instance.id}:")
51
51
  Paperclip::Task.log_error(" " + e.message + "\n")
52
52
  end
@@ -64,7 +64,8 @@ namespace :paperclip do
64
64
  names = Paperclip::Task.obtain_attachments(klass)
65
65
  names.each do |name|
66
66
  Paperclip.each_instance_with_attachment(klass, name) do |instance|
67
- if file = Paperclip.io_adapters.for(instance.send(name))
67
+ attachment = instance.send(name)
68
+ if file = Paperclip.io_adapters.for(attachment, attachment.options[:adapter_options])
68
69
  instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
69
70
  instance.send("#{name}_content_type=", file.content_type.to_s.strip)
70
71
  instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
@@ -90,6 +91,19 @@ namespace :paperclip do
90
91
  end
91
92
  Paperclip.save_current_attachments_styles!
92
93
  end
94
+
95
+ desc "Regenerates fingerprints for a given CLASS (and optional ATTACHMENT). Useful when changing digest."
96
+ task :fingerprints => :environment do
97
+ klass = Paperclip::Task.obtain_class
98
+ names = Paperclip::Task.obtain_attachments(klass)
99
+ names.each do |name|
100
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
101
+ attachment = instance.send(name)
102
+ attachment.assign(attachment)
103
+ instance.save(:validate => false)
104
+ end
105
+ end
106
+ end
93
107
  end
94
108
 
95
109
  desc "Cleans out invalid attachments. Useful after you've added new validations."
@@ -108,4 +122,20 @@ namespace :paperclip do
108
122
  end
109
123
  end
110
124
  end
125
+
126
+ desc "find missing attachments. Useful to know which attachments are broken"
127
+ task :find_broken_attachments => :environment do
128
+ klass = Paperclip::Task.obtain_class
129
+ names = Paperclip::Task.obtain_attachments(klass)
130
+ names.each do |name|
131
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
132
+ attachment = instance.send(name)
133
+ if attachment.exists?
134
+ print "."
135
+ else
136
+ Paperclip::Task.log_error("#{instance.class}##{attachment.name}, #{instance.id}, #{attachment.url}")
137
+ end
138
+ end
139
+ end
140
+ end
111
141
  end
data/paperclip.gemspec CHANGED
@@ -12,40 +12,43 @@ Gem::Specification.new do |s|
12
12
  s.description = "Easy upload management for ActiveRecord"
13
13
  s.license = "MIT"
14
14
 
15
- s.rubyforge_project = "paperclip"
16
-
17
15
  s.files = `git ls-files`.split("\n")
18
16
  s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
19
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
18
  s.require_paths = ["lib"]
21
19
 
20
+ if File.exist?('UPGRADING')
21
+ s.post_install_message = File.read("UPGRADING")
22
+ end
23
+
22
24
  s.requirements << "ImageMagick"
23
- s.required_ruby_version = ">= 1.9.2"
25
+ s.required_ruby_version = ">= 2.1.0"
24
26
 
25
- s.add_dependency('activemodel', '>= 3.0.0')
26
- s.add_dependency('activesupport', '>= 3.0.0')
27
- s.add_dependency('cocaine', '~> 0.5.3')
27
+ s.add_dependency('activemodel', '>= 4.2.0')
28
+ s.add_dependency('activesupport', '>= 4.2.0')
29
+ s.add_dependency('terrapin', '~> 0.6.0')
28
30
  s.add_dependency('mime-types')
31
+ s.add_dependency('mimemagic', '~> 0.3.0')
29
32
 
30
- s.add_development_dependency('activerecord', '>= 3.0.0')
33
+ s.add_development_dependency('activerecord', '>= 4.2.0')
31
34
  s.add_development_dependency('shoulda')
32
- s.add_development_dependency('rspec')
35
+ s.add_development_dependency('rspec', '~> 3.0')
33
36
  s.add_development_dependency('appraisal')
34
37
  s.add_development_dependency('mocha')
35
- s.add_development_dependency('aws-sdk', '>= 1.5.7')
38
+ s.add_development_dependency('aws-sdk-s3')
36
39
  s.add_development_dependency('bourne')
37
- s.add_development_dependency('cucumber', '~> 1.3.11')
38
- s.add_development_dependency('aruba')
40
+ s.add_development_dependency('cucumber-rails')
41
+ s.add_development_dependency('cucumber-expressions', '4.0.3') # TODO: investigate failures on 4.0.4
42
+ s.add_development_dependency('aruba', '~> 0.9.0')
39
43
  s.add_development_dependency('nokogiri')
40
- # Ruby version < 1.9.3 can't install capybara > 2.0.3.
41
- s.add_development_dependency('capybara', '= 2.0.3')
44
+ s.add_development_dependency('capybara')
42
45
  s.add_development_dependency('bundler')
43
- s.add_development_dependency('fog', '~> 1.0')
46
+ s.add_development_dependency('fog-aws')
47
+ s.add_development_dependency('fog-local')
44
48
  s.add_development_dependency('launchy')
45
49
  s.add_development_dependency('rake')
46
50
  s.add_development_dependency('fakeweb')
47
51
  s.add_development_dependency('railties')
48
- s.add_development_dependency('actionmailer', '>= 3.0.0')
49
52
  s.add_development_dependency('generator_spec')
50
53
  s.add_development_dependency('timecop')
51
54
  end
@@ -8,6 +8,6 @@ describe "Attachment Definitions" do
8
8
  Dummy.do_not_validate_attachment_file_type :avatar
9
9
  expected = {avatar: {path: "abc"}, other_attachment: {url: "123"}}
10
10
 
11
- assert_equal expected, Dummy.attachment_definitions
11
+ expect(Dummy.attachment_definitions).to eq expected
12
12
  end
13
13
  end
@@ -1,12 +1,9 @@
1
- # encoding: utf-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe 'Attachment Processing' do
5
- context 'using validates_attachment_content_type' do
6
- before do
7
- rebuild_class
8
- end
4
+ before { rebuild_class }
9
5
 
6
+ context 'using validates_attachment_content_type' do
10
7
  it 'processes attachments given a valid assignment' do
11
8
  file = File.new(fixture_file("5k.png"))
12
9
  Dummy.validates_attachment_content_type :avatar, content_type: "image/png"
@@ -31,8 +31,8 @@ describe 'Attachment Registry' do
31
31
  it 'calls the block with the class, attachment name, and options' do
32
32
  foo = Class.new
33
33
  expected_accumulations = [
34
- [foo, :avatar, { yo: 'greeting' }],
35
- [foo, :greeter, { ciao: 'greeting' }]
34
+ [foo, :avatar, { yo: "greeting" }],
35
+ [foo, :greeter, { ciao: "greeting" }]
36
36
  ]
37
37
  expected_accumulations.each do |args|
38
38
  Paperclip::AttachmentRegistry.register(*args)
@@ -50,25 +50,92 @@ describe 'Attachment Registry' do
50
50
  context '.definitions_for' do
51
51
  it 'produces the attachment name and options' do
52
52
  expected_definitions = {
53
- avatar: { yo: 'greeting' },
54
- greeter: { ciao: 'greeting' }
53
+ avatar: { yo: "greeting" },
54
+ greeter: { ciao: "greeting" }
55
55
  }
56
56
  foo = Class.new
57
- Paperclip::AttachmentRegistry.register(foo, :avatar, { yo: 'greeting' })
58
- Paperclip::AttachmentRegistry.register(foo, :greeter, { ciao: 'greeting' })
57
+ Paperclip::AttachmentRegistry.register(
58
+ foo,
59
+ :avatar,
60
+ yo: "greeting"
61
+ )
62
+ Paperclip::AttachmentRegistry.register(
63
+ foo,
64
+ :greeter,
65
+ ciao: "greeting"
66
+ )
59
67
 
60
68
  definitions = Paperclip::AttachmentRegistry.definitions_for(foo)
61
69
 
62
70
  assert_equal expected_definitions, definitions
63
71
  end
64
72
 
65
- it "produces defintions for subclasses" do
66
- expected_definitions = { avatar: { yo: 'greeting' } }
67
- Foo = Class.new
68
- Bar = Class.new(Foo)
69
- Paperclip::AttachmentRegistry.register(Foo, :avatar, expected_definitions[:avatar])
73
+ it 'produces defintions for subclasses' do
74
+ expected_definitions = { avatar: { yo: "greeting" } }
75
+ foo = Class.new
76
+ bar = Class.new(foo)
77
+ Paperclip::AttachmentRegistry.register(
78
+ foo,
79
+ :avatar,
80
+ expected_definitions[:avatar]
81
+ )
82
+
83
+ definitions = Paperclip::AttachmentRegistry.definitions_for(bar)
84
+
85
+ assert_equal expected_definitions, definitions
86
+ end
87
+
88
+ it 'produces defintions for subclasses but deep merging them' do
89
+ foo_definitions = { avatar: { yo: "greeting" } }
90
+ bar_definitions = { avatar: { ciao: "greeting" } }
91
+ expected_definitions = {
92
+ avatar: {
93
+ yo: "greeting",
94
+ ciao: "greeting"
95
+ }
96
+ }
97
+ foo = Class.new
98
+ bar = Class.new(foo)
99
+ Paperclip::AttachmentRegistry.register(
100
+ foo,
101
+ :avatar,
102
+ foo_definitions[:avatar]
103
+ )
104
+ Paperclip::AttachmentRegistry.register(
105
+ bar,
106
+ :avatar,
107
+ bar_definitions[:avatar]
108
+ )
109
+
110
+ definitions = Paperclip::AttachmentRegistry.definitions_for(bar)
70
111
 
71
- definitions = Paperclip::AttachmentRegistry.definitions_for(Bar)
112
+ assert_equal expected_definitions, definitions
113
+ end
114
+
115
+ it 'allows subclasses to override attachment defitions' do
116
+ foo_definitions = { avatar: { yo: "greeting" } }
117
+ bar_definitions = { avatar: { yo: "hello" } }
118
+
119
+ expected_definitions = {
120
+ avatar: {
121
+ yo: "hello"
122
+ }
123
+ }
124
+
125
+ foo = Class.new
126
+ bar = Class.new(foo)
127
+ Paperclip::AttachmentRegistry.register(
128
+ foo,
129
+ :avatar,
130
+ foo_definitions[:avatar]
131
+ )
132
+ Paperclip::AttachmentRegistry.register(
133
+ bar,
134
+ :avatar,
135
+ bar_definitions[:avatar]
136
+ )
137
+
138
+ definitions = Paperclip::AttachmentRegistry.definitions_for(bar)
72
139
 
73
140
  assert_equal expected_definitions, definitions
74
141
  end
@@ -77,7 +144,11 @@ describe 'Attachment Registry' do
77
144
  context '.clear' do
78
145
  it 'removes all of the existing attachment definitions' do
79
146
  foo = Class.new
80
- Paperclip::AttachmentRegistry.register(foo, :greeter, { ciao: 'greeting' })
147
+ Paperclip::AttachmentRegistry.register(
148
+ foo,
149
+ :greeter,
150
+ ciao: "greeting"
151
+ )
81
152
 
82
153
  Paperclip::AttachmentRegistry.clear
83
154