kt-paperclip 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +17 -0
  3. data/.github/issue_template.md +3 -0
  4. data/.gitignore +19 -0
  5. data/.hound.yml +1050 -0
  6. data/.rubocop.yml +1 -0
  7. data/.travis.yml +47 -0
  8. data/Appraisals +24 -0
  9. data/CONTRIBUTING.md +86 -0
  10. data/Gemfile +18 -0
  11. data/LICENSE +24 -0
  12. data/NEWS +515 -0
  13. data/README.md +1053 -0
  14. data/RELEASING.md +17 -0
  15. data/Rakefile +52 -0
  16. data/UPGRADING +17 -0
  17. data/features/basic_integration.feature +85 -0
  18. data/features/migration.feature +29 -0
  19. data/features/rake_tasks.feature +62 -0
  20. data/features/step_definitions/attachment_steps.rb +110 -0
  21. data/features/step_definitions/html_steps.rb +15 -0
  22. data/features/step_definitions/rails_steps.rb +257 -0
  23. data/features/step_definitions/s3_steps.rb +14 -0
  24. data/features/step_definitions/web_steps.rb +106 -0
  25. data/features/support/env.rb +12 -0
  26. data/features/support/fakeweb.rb +11 -0
  27. data/features/support/file_helpers.rb +34 -0
  28. data/features/support/fixtures/boot_config.txt +15 -0
  29. data/features/support/fixtures/gemfile.txt +5 -0
  30. data/features/support/fixtures/preinitializer.txt +20 -0
  31. data/features/support/paths.rb +28 -0
  32. data/features/support/rails.rb +39 -0
  33. data/features/support/selectors.rb +19 -0
  34. data/gemfiles/4.2.gemfile +20 -0
  35. data/gemfiles/5.0.gemfile +20 -0
  36. data/gemfiles/5.1.gemfile +20 -0
  37. data/gemfiles/5.2.gemfile +20 -0
  38. data/gemfiles/6.0.gemfile +20 -0
  39. data/lib/generators/paperclip/USAGE +8 -0
  40. data/lib/generators/paperclip/paperclip_generator.rb +36 -0
  41. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
  42. data/lib/paperclip.rb +215 -0
  43. data/lib/paperclip/attachment.rb +617 -0
  44. data/lib/paperclip/attachment_registry.rb +60 -0
  45. data/lib/paperclip/callbacks.rb +42 -0
  46. data/lib/paperclip/content_type_detector.rb +80 -0
  47. data/lib/paperclip/errors.rb +34 -0
  48. data/lib/paperclip/file_command_content_type_detector.rb +28 -0
  49. data/lib/paperclip/filename_cleaner.rb +15 -0
  50. data/lib/paperclip/geometry.rb +157 -0
  51. data/lib/paperclip/geometry_detector_factory.rb +45 -0
  52. data/lib/paperclip/geometry_parser_factory.rb +31 -0
  53. data/lib/paperclip/glue.rb +17 -0
  54. data/lib/paperclip/has_attached_file.rb +116 -0
  55. data/lib/paperclip/helpers.rb +60 -0
  56. data/lib/paperclip/interpolations.rb +201 -0
  57. data/lib/paperclip/interpolations/plural_cache.rb +18 -0
  58. data/lib/paperclip/io_adapters/abstract_adapter.rb +75 -0
  59. data/lib/paperclip/io_adapters/attachment_adapter.rb +47 -0
  60. data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
  61. data/lib/paperclip/io_adapters/empty_string_adapter.rb +19 -0
  62. data/lib/paperclip/io_adapters/file_adapter.rb +26 -0
  63. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +16 -0
  64. data/lib/paperclip/io_adapters/identity_adapter.rb +17 -0
  65. data/lib/paperclip/io_adapters/nil_adapter.rb +37 -0
  66. data/lib/paperclip/io_adapters/registry.rb +36 -0
  67. data/lib/paperclip/io_adapters/stringio_adapter.rb +36 -0
  68. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +44 -0
  69. data/lib/paperclip/io_adapters/uri_adapter.rb +68 -0
  70. data/lib/paperclip/locales/en.yml +18 -0
  71. data/lib/paperclip/logger.rb +21 -0
  72. data/lib/paperclip/matchers.rb +64 -0
  73. data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
  74. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +101 -0
  75. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
  76. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +97 -0
  77. data/lib/paperclip/media_type_spoof_detector.rb +90 -0
  78. data/lib/paperclip/missing_attachment_styles.rb +84 -0
  79. data/lib/paperclip/processor.rb +56 -0
  80. data/lib/paperclip/processor_helpers.rb +52 -0
  81. data/lib/paperclip/rails_environment.rb +21 -0
  82. data/lib/paperclip/railtie.rb +31 -0
  83. data/lib/paperclip/schema.rb +81 -0
  84. data/lib/paperclip/storage.rb +3 -0
  85. data/lib/paperclip/storage/filesystem.rb +99 -0
  86. data/lib/paperclip/storage/fog.rb +252 -0
  87. data/lib/paperclip/storage/s3.rb +461 -0
  88. data/lib/paperclip/style.rb +106 -0
  89. data/lib/paperclip/tempfile.rb +42 -0
  90. data/lib/paperclip/tempfile_factory.rb +22 -0
  91. data/lib/paperclip/thumbnail.rb +131 -0
  92. data/lib/paperclip/url_generator.rb +76 -0
  93. data/lib/paperclip/validators.rb +73 -0
  94. data/lib/paperclip/validators/attachment_content_type_validator.rb +88 -0
  95. data/lib/paperclip/validators/attachment_file_name_validator.rb +75 -0
  96. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +28 -0
  97. data/lib/paperclip/validators/attachment_presence_validator.rb +28 -0
  98. data/lib/paperclip/validators/attachment_size_validator.rb +109 -0
  99. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +29 -0
  100. data/lib/paperclip/version.rb +3 -0
  101. data/lib/tasks/paperclip.rake +140 -0
  102. data/paperclip.gemspec +50 -0
  103. data/shoulda_macros/paperclip.rb +134 -0
  104. data/spec/database.yml +4 -0
  105. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  106. data/spec/paperclip/attachment_processing_spec.rb +79 -0
  107. data/spec/paperclip/attachment_registry_spec.rb +158 -0
  108. data/spec/paperclip/attachment_spec.rb +1590 -0
  109. data/spec/paperclip/content_type_detector_spec.rb +47 -0
  110. data/spec/paperclip/file_command_content_type_detector_spec.rb +40 -0
  111. data/spec/paperclip/filename_cleaner_spec.rb +13 -0
  112. data/spec/paperclip/geometry_detector_spec.rb +38 -0
  113. data/spec/paperclip/geometry_parser_spec.rb +73 -0
  114. data/spec/paperclip/geometry_spec.rb +255 -0
  115. data/spec/paperclip/glue_spec.rb +42 -0
  116. data/spec/paperclip/has_attached_file_spec.rb +78 -0
  117. data/spec/paperclip/integration_spec.rb +702 -0
  118. data/spec/paperclip/interpolations_spec.rb +270 -0
  119. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +160 -0
  120. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +140 -0
  121. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +88 -0
  122. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  123. data/spec/paperclip/io_adapters/file_adapter_spec.rb +131 -0
  124. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +137 -0
  125. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
  126. data/spec/paperclip/io_adapters/nil_adapter_spec.rb +25 -0
  127. data/spec/paperclip/io_adapters/registry_spec.rb +35 -0
  128. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
  129. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
  130. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +221 -0
  131. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
  132. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +108 -0
  133. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
  134. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
  135. data/spec/paperclip/media_type_spoof_detector_spec.rb +120 -0
  136. data/spec/paperclip/meta_class_spec.rb +30 -0
  137. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +88 -0
  138. data/spec/paperclip/paperclip_spec.rb +196 -0
  139. data/spec/paperclip/plural_cache_spec.rb +37 -0
  140. data/spec/paperclip/processor_helpers_spec.rb +57 -0
  141. data/spec/paperclip/processor_spec.rb +26 -0
  142. data/spec/paperclip/rails_environment_spec.rb +30 -0
  143. data/spec/paperclip/rake_spec.rb +103 -0
  144. data/spec/paperclip/schema_spec.rb +252 -0
  145. data/spec/paperclip/storage/filesystem_spec.rb +79 -0
  146. data/spec/paperclip/storage/fog_spec.rb +560 -0
  147. data/spec/paperclip/storage/s3_live_spec.rb +188 -0
  148. data/spec/paperclip/storage/s3_spec.rb +1695 -0
  149. data/spec/paperclip/style_spec.rb +251 -0
  150. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  151. data/spec/paperclip/tempfile_spec.rb +35 -0
  152. data/spec/paperclip/thumbnail_spec.rb +504 -0
  153. data/spec/paperclip/url_generator_spec.rb +221 -0
  154. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
  155. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +159 -0
  156. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
  157. data/spec/paperclip/validators/attachment_size_validator_spec.rb +235 -0
  158. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +48 -0
  159. data/spec/paperclip/validators_spec.rb +164 -0
  160. data/spec/spec_helper.rb +45 -0
  161. data/spec/support/assertions.rb +84 -0
  162. data/spec/support/fake_model.rb +24 -0
  163. data/spec/support/fake_rails.rb +12 -0
  164. data/spec/support/fixtures/12k.png +0 -0
  165. data/spec/support/fixtures/50x50.png +0 -0
  166. data/spec/support/fixtures/5k.png +0 -0
  167. data/spec/support/fixtures/animated +0 -0
  168. data/spec/support/fixtures/animated.gif +0 -0
  169. data/spec/support/fixtures/animated.unknown +0 -0
  170. data/spec/support/fixtures/bad.png +1 -0
  171. data/spec/support/fixtures/empty.html +1 -0
  172. data/spec/support/fixtures/empty.xlsx +0 -0
  173. data/spec/support/fixtures/fog.yml +8 -0
  174. data/spec/support/fixtures/rotated.jpg +0 -0
  175. data/spec/support/fixtures/s3.yml +8 -0
  176. data/spec/support/fixtures/spaced file.jpg +0 -0
  177. data/spec/support/fixtures/spaced file.png +0 -0
  178. data/spec/support/fixtures/text.txt +1 -0
  179. data/spec/support/fixtures/twopage.pdf +0 -0
  180. data/spec/support/fixtures/uppercase.PNG +0 -0
  181. data/spec/support/matchers/accept.rb +5 -0
  182. data/spec/support/matchers/exist.rb +5 -0
  183. data/spec/support/matchers/have_column.rb +23 -0
  184. data/spec/support/mock_attachment.rb +24 -0
  185. data/spec/support/mock_interpolator.rb +24 -0
  186. data/spec/support/mock_url_generator_builder.rb +26 -0
  187. data/spec/support/model_reconstruction.rb +72 -0
  188. data/spec/support/reporting.rb +11 -0
  189. data/spec/support/test_data.rb +13 -0
  190. data/spec/support/version_helper.rb +9 -0
  191. metadata +586 -0
@@ -0,0 +1,106 @@
1
+ module Paperclip
2
+ # The Style class holds the definition of a thumbnail style, applying
3
+ # whatever processing is required to normalize the definition and delaying
4
+ # the evaluation of block parameters until useful context is available.
5
+
6
+ class Style
7
+ attr_reader :name, :attachment, :format
8
+
9
+ # Creates a Style object. +name+ is the name of the attachment,
10
+ # +definition+ is the style definition from has_attached_file, which
11
+ # can be string, array or hash
12
+ def initialize(name, definition, attachment)
13
+ @name = name
14
+ @attachment = attachment
15
+ if definition.is_a? Hash
16
+ @geometry = definition.delete(:geometry)
17
+ @format = definition.delete(:format)
18
+ @processors = definition.delete(:processors)
19
+ @convert_options = definition.delete(:convert_options)
20
+ @source_file_options = definition.delete(:source_file_options)
21
+ @other_args = definition
22
+ elsif definition.is_a? String
23
+ @geometry = definition
24
+ @format = nil
25
+ @other_args = {}
26
+ else
27
+ @geometry, @format = [definition, nil].flatten[0..1]
28
+ @other_args = {}
29
+ end
30
+ @format = default_format if @format.blank?
31
+ end
32
+
33
+ # retrieves from the attachment the processors defined in the has_attached_file call
34
+ # (which method (in the attachment) will call any supplied procs)
35
+ # There is an important change of interface here: a style rule can set its own processors
36
+ # by default we behave as before, though.
37
+ # if a proc has been supplied, we call it here
38
+ def processors
39
+ @processors.respond_to?(:call) ? @processors.call(attachment.instance) : (@processors || attachment.processors)
40
+ end
41
+
42
+ # retrieves from the attachment the whiny setting
43
+ def whiny
44
+ attachment.whiny
45
+ end
46
+
47
+ # returns true if we're inclined to grumble
48
+ def whiny?
49
+ !!whiny
50
+ end
51
+
52
+ def convert_options
53
+ @convert_options.respond_to?(:call) ? @convert_options.call(attachment.instance) :
54
+ (@convert_options || attachment.send(:extra_options_for, name))
55
+ end
56
+
57
+ def source_file_options
58
+ @source_file_options.respond_to?(:call) ? @source_file_options.call(attachment.instance) :
59
+ (@source_file_options || attachment.send(:extra_source_file_options_for, name))
60
+ end
61
+
62
+ # returns the geometry string for this style
63
+ # if a proc has been supplied, we call it here
64
+ def geometry
65
+ @geometry.respond_to?(:call) ? @geometry.call(attachment.instance) : @geometry
66
+ end
67
+
68
+ # Supplies the hash of options that processors expect to receive as their second argument
69
+ # Arguments other than the standard geometry, format etc are just passed through from
70
+ # initialization and any procs are called here, just before post-processing.
71
+ def processor_options
72
+ args = { style: name }
73
+ @other_args.each do |k, v|
74
+ args[k] = v.respond_to?(:call) ? v.call(attachment) : v
75
+ end
76
+ [:processors, :geometry, :format, :whiny, :convert_options, :source_file_options].each do |k|
77
+ (arg = send(k)) && args[k] = arg
78
+ end
79
+ args
80
+ end
81
+
82
+ # Supports getting and setting style properties with hash notation to ensure backwards-compatibility
83
+ # eg. @attachment.styles[:large][:geometry]@ will still work
84
+ def [](key)
85
+ if [:name, :convert_options, :whiny, :processors, :geometry, :format, :animated, :source_file_options].include?(key)
86
+ send(key)
87
+ elsif defined? @other_args[key]
88
+ @other_args[key]
89
+ end
90
+ end
91
+
92
+ def []=(key, value)
93
+ if [:name, :convert_options, :whiny, :processors, :geometry, :format, :animated, :source_file_options].include?(key)
94
+ send("#{key}=".intern, value)
95
+ else
96
+ @other_args[key] = value
97
+ end
98
+ end
99
+
100
+ # defaults to default format (nil by default)
101
+ def default_format
102
+ base = attachment.options[:default_format]
103
+ base.respond_to?(:call) ? base.call(attachment, name) : base
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,42 @@
1
+ module Paperclip
2
+ # Overriding some implementation of Tempfile
3
+ class Tempfile < ::Tempfile
4
+ # Due to how ImageMagick handles its image format conversion and how
5
+ # Tempfile handles its naming scheme, it is necessary to override how
6
+ # Tempfile makes # its names so as to allow for file extensions. Idea
7
+ # taken from the comments on this blog post:
8
+ # http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
9
+ #
10
+ # This is Ruby 1.9.3's implementation.
11
+ def make_tmpname(prefix_suffix, n)
12
+ if RUBY_PLATFORM =~ /java/
13
+ case prefix_suffix
14
+ when String
15
+ prefix = prefix_suffix
16
+ suffix = ""
17
+ when Array
18
+ prefix, suffix = *prefix_suffix
19
+ else
20
+ raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
21
+ end
22
+
23
+ t = Time.now.strftime("%y%m%d")
24
+ path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
25
+ else
26
+ super
27
+ end
28
+ end
29
+ end
30
+
31
+ module TempfileEncoding
32
+ # This overrides Tempfile#binmode to make sure that the extenal encoding
33
+ # for binary mode is ASCII-8BIT. This behavior is what's in CRuby, but not
34
+ # in JRuby
35
+ def binmode
36
+ set_encoding("ASCII-8BIT")
37
+ super
38
+ end
39
+ end
40
+ end
41
+
42
+ ::Tempfile.include Paperclip::TempfileEncoding if RUBY_PLATFORM =~ /java/
@@ -0,0 +1,22 @@
1
+ module Paperclip
2
+ class TempfileFactory
3
+ def generate(name = random_name)
4
+ @name = name
5
+ file = Tempfile.new([basename, extension])
6
+ file.binmode
7
+ file
8
+ end
9
+
10
+ def extension
11
+ File.extname(@name)
12
+ end
13
+
14
+ def basename
15
+ Digest::MD5.hexdigest(File.basename(@name, extension))
16
+ end
17
+
18
+ def random_name
19
+ SecureRandom.uuid
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,131 @@
1
+ module Paperclip
2
+ # Handles thumbnailing images that are uploaded.
3
+ class Thumbnail < Processor
4
+ attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options,
5
+ :source_file_options, :animated, :auto_orient, :frame_index
6
+
7
+ # List of formats that we need to preserve animation
8
+ ANIMATED_FORMATS = %w(gif).freeze
9
+ MULTI_FRAME_FORMATS = %w(.mkv .avi .mp4 .mov .mpg .mpeg .gif).freeze
10
+
11
+ # Creates a Thumbnail object set to work on the +file+ given. It
12
+ # will attempt to transform the image into one defined by +target_geometry+
13
+ # which is a "WxH"-style string. +format+ will be inferred from the +file+
14
+ # unless specified. Thumbnail creation will raise no errors unless
15
+ # +whiny+ is true (which it is, by default. If +convert_options+ is
16
+ # set, the options will be appended to the convert command upon image conversion
17
+ #
18
+ # Options include:
19
+ #
20
+ # +geometry+ - the desired width and height of the thumbnail (required)
21
+ # +file_geometry_parser+ - an object with a method named +from_file+ that takes an image file and produces its geometry and a +transformation_to+. Defaults to Paperclip::Geometry
22
+ # +string_geometry_parser+ - an object with a method named +parse+ that takes a string and produces an object with +width+, +height+, and +to_s+ accessors. Defaults to Paperclip::Geometry
23
+ # +source_file_options+ - flags passed to the +convert+ command that influence how the source file is read
24
+ # +convert_options+ - flags passed to the +convert+ command that influence how the image is processed
25
+ # +whiny+ - whether to raise an error when processing fails. Defaults to true
26
+ # +format+ - the desired filename extension
27
+ # +animated+ - whether to merge all the layers in the image. Defaults to true
28
+ # +frame_index+ - the frame index of the source file to render as the thumbnail
29
+ def initialize(file, options = {}, attachment = nil)
30
+ super
31
+
32
+ geometry = options[:geometry].to_s
33
+ @crop = geometry[-1, 1] == "#"
34
+ @target_geometry = options.fetch(:string_geometry_parser, Geometry).parse(geometry)
35
+ @current_geometry = options.fetch(:file_geometry_parser, Geometry).from_file(@file)
36
+ @source_file_options = options[:source_file_options]
37
+ @convert_options = options[:convert_options]
38
+ @whiny = options.fetch(:whiny, true)
39
+ @format = options[:format]
40
+ @animated = options.fetch(:animated, true)
41
+ @auto_orient = options.fetch(:auto_orient, true)
42
+ @current_geometry.auto_orient if @auto_orient && @current_geometry.respond_to?(:auto_orient)
43
+ @source_file_options = @source_file_options.split(/\s+/) if @source_file_options.respond_to?(:split)
44
+ @convert_options = @convert_options.split(/\s+/) if @convert_options.respond_to?(:split)
45
+
46
+ @current_format = File.extname(@file.path)
47
+ @basename = File.basename(@file.path, @current_format)
48
+ @frame_index = multi_frame_format? ? options.fetch(:frame_index, 0) : 0
49
+ end
50
+
51
+ # Returns true if the +target_geometry+ is meant to crop.
52
+ def crop?
53
+ @crop
54
+ end
55
+
56
+ # Returns true if the image is meant to make use of additional convert options.
57
+ def convert_options?
58
+ !@convert_options.nil? && !@convert_options.empty?
59
+ end
60
+
61
+ # Performs the conversion of the +file+ into a thumbnail. Returns the Tempfile
62
+ # that contains the new image.
63
+ def make
64
+ src = @file
65
+ filename = [@basename, @format ? ".#{@format}" : ""].join
66
+ dst = TempfileFactory.new.generate(filename)
67
+
68
+ begin
69
+ parameters = []
70
+ parameters << source_file_options
71
+ parameters << ":source"
72
+ parameters << transformation_command
73
+ parameters << convert_options
74
+ parameters << ":dest"
75
+
76
+ parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")
77
+
78
+ frame = animated? ? "" : "[#{@frame_index}]"
79
+ convert(
80
+ parameters,
81
+ source: "#{File.expand_path(src.path)}#{frame}",
82
+ dest: File.expand_path(dst.path)
83
+ )
84
+ rescue Terrapin::ExitStatusError => e
85
+ if @whiny
86
+ message = "There was an error processing the thumbnail for #{@basename}:\n" + e.message
87
+ raise Paperclip::Error, message
88
+ end
89
+ rescue Terrapin::CommandNotFoundError => e
90
+ raise Paperclip::Errors::CommandNotFoundError.new("Could not run the `convert` command. Please install ImageMagick.")
91
+ end
92
+
93
+ dst
94
+ end
95
+
96
+ # Returns the command ImageMagick's +convert+ needs to transform the image
97
+ # into the thumbnail.
98
+ def transformation_command
99
+ scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
100
+ trans = []
101
+ trans << "-coalesce" if animated?
102
+ trans << "-auto-orient" if auto_orient
103
+ trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
104
+ trans << "-crop" << %["#{crop}"] << "+repage" if crop
105
+ trans << '-layers "optimize"' if animated?
106
+ trans
107
+ end
108
+
109
+ protected
110
+
111
+ def multi_frame_format?
112
+ MULTI_FRAME_FORMATS.include? @current_format
113
+ end
114
+
115
+ def animated?
116
+ @animated && (ANIMATED_FORMATS.include?(@format.to_s) || @format.blank?) && identified_as_animated?
117
+ end
118
+
119
+ # Return true if ImageMagick's +identify+ returns an animated format
120
+ def identified_as_animated?
121
+ if @identified_as_animated.nil?
122
+ @identified_as_animated = ANIMATED_FORMATS.include? identify("-format %m :file", file: "#{@file.path}[0]").to_s.downcase.strip
123
+ end
124
+ @identified_as_animated
125
+ rescue Terrapin::ExitStatusError => e
126
+ raise Paperclip::Error, "There was an error running `identify` for #{@basename}" if @whiny
127
+ rescue Terrapin::CommandNotFoundError => e
128
+ raise Paperclip::Errors::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,76 @@
1
+ require "uri"
2
+ require "active_support/core_ext/module/delegation"
3
+
4
+ module Paperclip
5
+ class UrlGenerator
6
+ def initialize(attachment)
7
+ @attachment = attachment
8
+ end
9
+
10
+ def for(style_name, 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)
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :attachment
22
+ delegate :options, to: :attachment, prefix: true
23
+
24
+ # This method is all over the place.
25
+ def 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])
30
+ else
31
+ attachment_options[:default_url]
32
+ end
33
+ end
34
+
35
+ def most_appropriate_url
36
+ if @attachment.original_filename.nil?
37
+ default_url
38
+ else
39
+ attachment_options[:url]
40
+ end
41
+ end
42
+
43
+ def timestamp_as_needed(url, options)
44
+ if options[:timestamp] && timestamp_possible?
45
+ delimiter_char = url.match(/\?.+=/) ? "&" : "?"
46
+ "#{url}#{delimiter_char}#{@attachment.updated_at}"
47
+ else
48
+ url
49
+ end
50
+ end
51
+
52
+ def timestamp_possible?
53
+ @attachment.respond_to?(:updated_at) && @attachment.updated_at.present?
54
+ end
55
+
56
+ def escape_url_as_needed(url, options)
57
+ if options[:escape]
58
+ escape_url(url)
59
+ else
60
+ url
61
+ end
62
+ end
63
+
64
+ def escape_url(url)
65
+ if url.respond_to?(:escape)
66
+ url.escape
67
+ else
68
+ URI.escape(url).gsub(escape_regex) { |m| "%#{m.ord.to_s(16).upcase}" }
69
+ end
70
+ end
71
+
72
+ def escape_regex
73
+ /[\?\(\)\[\]\+]/
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,73 @@
1
+ require "active_model"
2
+ require "active_support/concern"
3
+ require "active_support/core_ext/array/wrap"
4
+ require "paperclip/validators/attachment_content_type_validator"
5
+ require "paperclip/validators/attachment_file_name_validator"
6
+ require "paperclip/validators/attachment_presence_validator"
7
+ require "paperclip/validators/attachment_size_validator"
8
+ require "paperclip/validators/media_type_spoof_detection_validator"
9
+ require "paperclip/validators/attachment_file_type_ignorance_validator"
10
+
11
+ module Paperclip
12
+ module Validators
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ extend HelperMethods
17
+ include HelperMethods
18
+ end
19
+
20
+ ::Paperclip::REQUIRED_VALIDATORS = [AttachmentFileNameValidator, AttachmentContentTypeValidator, AttachmentFileTypeIgnoranceValidator].freeze
21
+
22
+ module ClassMethods
23
+ # This method is a shortcut to validator classes that is in
24
+ # "Attachment...Validator" format. It is almost the same thing as the
25
+ # +validates+ method that shipped with Rails, but this is customized to
26
+ # be using with attachment validators. This is helpful when you're using
27
+ # multiple attachment validators on a single attachment.
28
+ #
29
+ # Example of using the validator:
30
+ #
31
+ # validates_attachment :avatar, :presence => true,
32
+ # :content_type => { :content_type => "image/jpg" },
33
+ # :size => { :in => 0..10.kilobytes }
34
+ #
35
+ def validates_attachment(*attributes)
36
+ options = attributes.extract_options!.dup
37
+
38
+ Paperclip::Validators.constants.each do |constant|
39
+ if constant.to_s =~ /\AAttachment(.+)Validator\z/
40
+ validator_kind = $1.underscore.to_sym
41
+
42
+ if options.key?(validator_kind)
43
+ validator_options = options.delete(validator_kind)
44
+ validator_options = {} if validator_options == true
45
+ conditional_options = options.slice(:if, :unless)
46
+ Array.wrap(validator_options).each do |local_options|
47
+ method_name = Paperclip::Validators.const_get(constant.to_s).helper_method_name
48
+ send(method_name, attributes, local_options.merge(conditional_options))
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ def validate_before_processing(validator_class, options)
56
+ options = options.dup
57
+ attributes = options.delete(:attributes)
58
+ attributes.each do |attribute|
59
+ options[:attributes] = [attribute]
60
+ create_validating_before_filter(attribute, validator_class, options)
61
+ end
62
+ end
63
+
64
+ def create_validating_before_filter(attribute, validator_class, options)
65
+ if_clause = options.delete(:if)
66
+ unless_clause = options.delete(:unless)
67
+ send(:"before_#{attribute}_validate", if: if_clause, unless: unless_clause) do |*_args|
68
+ validator_class.new(options.dup).validate(self)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end