kt-paperclip 4.4.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 (198) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.hound.yml +1066 -0
  4. data/.rubocop.yml +1 -0
  5. data/.travis.yml +23 -0
  6. data/Appraisals +11 -0
  7. data/CONTRIBUTING.md +75 -0
  8. data/Gemfile +21 -0
  9. data/LICENSE +24 -0
  10. data/NEWS +420 -0
  11. data/README.md +977 -0
  12. data/RELEASING.md +17 -0
  13. data/Rakefile +44 -0
  14. data/UPGRADING +14 -0
  15. data/cucumber/paperclip_steps.rb +6 -0
  16. data/features/basic_integration.feature +80 -0
  17. data/features/migration.feature +94 -0
  18. data/features/rake_tasks.feature +62 -0
  19. data/features/step_definitions/attachment_steps.rb +110 -0
  20. data/features/step_definitions/html_steps.rb +15 -0
  21. data/features/step_definitions/rails_steps.rb +236 -0
  22. data/features/step_definitions/s3_steps.rb +14 -0
  23. data/features/step_definitions/web_steps.rb +107 -0
  24. data/features/support/env.rb +11 -0
  25. data/features/support/fakeweb.rb +13 -0
  26. data/features/support/file_helpers.rb +34 -0
  27. data/features/support/fixtures/boot_config.txt +15 -0
  28. data/features/support/fixtures/gemfile.txt +5 -0
  29. data/features/support/fixtures/preinitializer.txt +20 -0
  30. data/features/support/paths.rb +28 -0
  31. data/features/support/rails.rb +63 -0
  32. data/features/support/selectors.rb +19 -0
  33. data/gemfiles/3.2.gemfile +19 -0
  34. data/gemfiles/4.1.gemfile +19 -0
  35. data/gemfiles/4.2.gemfile +19 -0
  36. data/lib/generators/paperclip/USAGE +8 -0
  37. data/lib/generators/paperclip/paperclip_generator.rb +30 -0
  38. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
  39. data/lib/kt-paperclip.rb +1 -0
  40. data/lib/paperclip/attachment.rb +608 -0
  41. data/lib/paperclip/attachment_registry.rb +59 -0
  42. data/lib/paperclip/callbacks.rb +40 -0
  43. data/lib/paperclip/content_type_detector.rb +79 -0
  44. data/lib/paperclip/deprecations.rb +42 -0
  45. data/lib/paperclip/errors.rb +32 -0
  46. data/lib/paperclip/file_command_content_type_detector.rb +30 -0
  47. data/lib/paperclip/filename_cleaner.rb +16 -0
  48. data/lib/paperclip/geometry.rb +158 -0
  49. data/lib/paperclip/geometry_detector_factory.rb +48 -0
  50. data/lib/paperclip/geometry_parser_factory.rb +31 -0
  51. data/lib/paperclip/glue.rb +17 -0
  52. data/lib/paperclip/has_attached_file.rb +109 -0
  53. data/lib/paperclip/helpers.rb +56 -0
  54. data/lib/paperclip/interpolations/plural_cache.rb +18 -0
  55. data/lib/paperclip/interpolations.rb +197 -0
  56. data/lib/paperclip/io_adapters/abstract_adapter.rb +47 -0
  57. data/lib/paperclip/io_adapters/attachment_adapter.rb +36 -0
  58. data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
  59. data/lib/paperclip/io_adapters/empty_string_adapter.rb +18 -0
  60. data/lib/paperclip/io_adapters/file_adapter.rb +22 -0
  61. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +16 -0
  62. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
  63. data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
  64. data/lib/paperclip/io_adapters/registry.rb +32 -0
  65. data/lib/paperclip/io_adapters/stringio_adapter.rb +33 -0
  66. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +42 -0
  67. data/lib/paperclip/io_adapters/uri_adapter.rb +63 -0
  68. data/lib/paperclip/locales/de.yml +18 -0
  69. data/lib/paperclip/locales/en.yml +18 -0
  70. data/lib/paperclip/locales/es.yml +18 -0
  71. data/lib/paperclip/locales/ja.yml +18 -0
  72. data/lib/paperclip/locales/pt-BR.yml +18 -0
  73. data/lib/paperclip/locales/zh-CN.yml +18 -0
  74. data/lib/paperclip/locales/zh-HK.yml +18 -0
  75. data/lib/paperclip/locales/zh-TW.yml +18 -0
  76. data/lib/paperclip/logger.rb +21 -0
  77. data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
  78. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +100 -0
  79. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
  80. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +96 -0
  81. data/lib/paperclip/matchers.rb +64 -0
  82. data/lib/paperclip/media_type_spoof_detector.rb +89 -0
  83. data/lib/paperclip/missing_attachment_styles.rb +79 -0
  84. data/lib/paperclip/processor.rb +48 -0
  85. data/lib/paperclip/processor_helpers.rb +50 -0
  86. data/lib/paperclip/rails_environment.rb +25 -0
  87. data/lib/paperclip/railtie.rb +31 -0
  88. data/lib/paperclip/schema.rb +83 -0
  89. data/lib/paperclip/storage/filesystem.rb +90 -0
  90. data/lib/paperclip/storage/fog.rb +242 -0
  91. data/lib/paperclip/storage/s3.rb +440 -0
  92. data/lib/paperclip/storage.rb +3 -0
  93. data/lib/paperclip/style.rb +109 -0
  94. data/lib/paperclip/tempfile.rb +43 -0
  95. data/lib/paperclip/tempfile_factory.rb +23 -0
  96. data/lib/paperclip/thumbnail.rb +121 -0
  97. data/lib/paperclip/url_generator.rb +79 -0
  98. data/lib/paperclip/validators/attachment_content_type_validator.rb +88 -0
  99. data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
  100. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
  101. data/lib/paperclip/validators/attachment_presence_validator.rb +30 -0
  102. data/lib/paperclip/validators/attachment_size_validator.rb +115 -0
  103. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
  104. data/lib/paperclip/validators.rb +74 -0
  105. data/lib/paperclip/version.rb +3 -0
  106. data/lib/paperclip.rb +213 -0
  107. data/lib/tasks/paperclip.rake +127 -0
  108. data/paperclip.gemspec +52 -0
  109. data/shoulda_macros/paperclip.rb +134 -0
  110. data/spec/database.yml +4 -0
  111. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  112. data/spec/paperclip/attachment_processing_spec.rb +82 -0
  113. data/spec/paperclip/attachment_registry_spec.rb +130 -0
  114. data/spec/paperclip/attachment_spec.rb +1494 -0
  115. data/spec/paperclip/content_type_detector_spec.rb +48 -0
  116. data/spec/paperclip/deprecations_spec.rb +65 -0
  117. data/spec/paperclip/file_command_content_type_detector_spec.rb +26 -0
  118. data/spec/paperclip/filename_cleaner_spec.rb +14 -0
  119. data/spec/paperclip/geometry_detector_spec.rb +39 -0
  120. data/spec/paperclip/geometry_parser_spec.rb +73 -0
  121. data/spec/paperclip/geometry_spec.rb +255 -0
  122. data/spec/paperclip/glue_spec.rb +44 -0
  123. data/spec/paperclip/has_attached_file_spec.rb +142 -0
  124. data/spec/paperclip/integration_spec.rb +667 -0
  125. data/spec/paperclip/interpolations_spec.rb +262 -0
  126. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +78 -0
  127. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +139 -0
  128. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +83 -0
  129. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  130. data/spec/paperclip/io_adapters/file_adapter_spec.rb +131 -0
  131. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +104 -0
  132. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
  133. data/spec/paperclip/io_adapters/nil_adapter_spec.rb +25 -0
  134. data/spec/paperclip/io_adapters/registry_spec.rb +35 -0
  135. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
  136. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
  137. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +127 -0
  138. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
  139. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +99 -0
  140. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
  141. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
  142. data/spec/paperclip/media_type_spoof_detector_spec.rb +79 -0
  143. data/spec/paperclip/meta_class_spec.rb +30 -0
  144. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
  145. data/spec/paperclip/paperclip_spec.rb +222 -0
  146. data/spec/paperclip/plural_cache_spec.rb +37 -0
  147. data/spec/paperclip/processor_helpers_spec.rb +57 -0
  148. data/spec/paperclip/processor_spec.rb +26 -0
  149. data/spec/paperclip/rails_environment_spec.rb +33 -0
  150. data/spec/paperclip/rake_spec.rb +103 -0
  151. data/spec/paperclip/schema_spec.rb +248 -0
  152. data/spec/paperclip/storage/filesystem_spec.rb +79 -0
  153. data/spec/paperclip/storage/fog_spec.rb +540 -0
  154. data/spec/paperclip/storage/s3_live_spec.rb +182 -0
  155. data/spec/paperclip/storage/s3_spec.rb +1526 -0
  156. data/spec/paperclip/style_spec.rb +255 -0
  157. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  158. data/spec/paperclip/thumbnail_spec.rb +500 -0
  159. data/spec/paperclip/url_generator_spec.rb +221 -0
  160. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
  161. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
  162. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
  163. data/spec/paperclip/validators/attachment_size_validator_spec.rb +229 -0
  164. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
  165. data/spec/paperclip/validators_spec.rb +164 -0
  166. data/spec/spec_helper.rb +43 -0
  167. data/spec/support/assertions.rb +71 -0
  168. data/spec/support/deprecations.rb +9 -0
  169. data/spec/support/fake_model.rb +25 -0
  170. data/spec/support/fake_rails.rb +12 -0
  171. data/spec/support/fixtures/12k.png +0 -0
  172. data/spec/support/fixtures/50x50.png +0 -0
  173. data/spec/support/fixtures/5k.png +0 -0
  174. data/spec/support/fixtures/animated +0 -0
  175. data/spec/support/fixtures/animated.gif +0 -0
  176. data/spec/support/fixtures/animated.unknown +0 -0
  177. data/spec/support/fixtures/bad.png +1 -0
  178. data/spec/support/fixtures/empty.html +1 -0
  179. data/spec/support/fixtures/empty.xlsx +0 -0
  180. data/spec/support/fixtures/fog.yml +8 -0
  181. data/spec/support/fixtures/rotated.jpg +0 -0
  182. data/spec/support/fixtures/s3.yml +8 -0
  183. data/spec/support/fixtures/spaced file.jpg +0 -0
  184. data/spec/support/fixtures/spaced file.png +0 -0
  185. data/spec/support/fixtures/text.txt +1 -0
  186. data/spec/support/fixtures/twopage.pdf +0 -0
  187. data/spec/support/fixtures/uppercase.PNG +0 -0
  188. data/spec/support/matchers/accept.rb +5 -0
  189. data/spec/support/matchers/exist.rb +5 -0
  190. data/spec/support/matchers/have_column.rb +23 -0
  191. data/spec/support/mock_attachment.rb +22 -0
  192. data/spec/support/mock_interpolator.rb +24 -0
  193. data/spec/support/mock_url_generator_builder.rb +27 -0
  194. data/spec/support/model_reconstruction.rb +60 -0
  195. data/spec/support/rails_helpers.rb +7 -0
  196. data/spec/support/test_data.rb +13 -0
  197. data/spec/support/version_helper.rb +9 -0
  198. metadata +648 -0
@@ -0,0 +1,1494 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Paperclip::Attachment do
5
+
6
+ it "is not present when file not set" do
7
+ rebuild_class
8
+ dummy = Dummy.new
9
+ expect(dummy.avatar).to be_blank
10
+ expect(dummy.avatar).to_not be_present
11
+ end
12
+
13
+ it "is present when the file is set" do
14
+ rebuild_class
15
+ dummy = Dummy.new
16
+ dummy.avatar = File.new(fixture_file("50x50.png"), "rb")
17
+ expect(dummy.avatar).to_not be_blank
18
+ expect(dummy.avatar).to be_present
19
+ end
20
+
21
+ it "processes :original style first" do
22
+ file = File.new(fixture_file("50x50.png"), 'rb')
23
+ rebuild_class styles: { small: '100x>', original: '42x42#' }
24
+ dummy = Dummy.new
25
+ dummy.avatar = file
26
+ dummy.save
27
+
28
+ # :small avatar should be 42px wide (processed original), not 50px (preprocessed original)
29
+ expect(`identify -format "%w" "#{dummy.avatar.path(:small)}"`.strip).to eq "42"
30
+
31
+ file.close
32
+ end
33
+
34
+ it "does not delete styles that don't get reprocessed" do
35
+ file = File.new(fixture_file("50x50.png"), 'rb')
36
+ rebuild_class styles: {
37
+ small: "100x>",
38
+ large: "500x>",
39
+ original: "42x42#"
40
+ }
41
+
42
+ dummy = Dummy.new
43
+ dummy.avatar = file
44
+ dummy.save
45
+
46
+ expect(dummy.avatar.path(:small)).to exist
47
+ expect(dummy.avatar.path(:large)).to exist
48
+ expect(dummy.avatar.path(:original)).to exist
49
+
50
+ dummy.avatar.reprocess!(:small)
51
+
52
+ expect(dummy.avatar.path(:small)).to exist
53
+ expect(dummy.avatar.path(:large)).to exist
54
+ expect(dummy.avatar.path(:original)).to exist
55
+ end
56
+
57
+ context "having a not empty hash as a default option" do
58
+ before do
59
+ @old_default_options = Paperclip::Attachment.default_options.dup
60
+ @new_default_options = { convert_options: { all: "-background white" } }
61
+ Paperclip::Attachment.default_options.merge!(@new_default_options)
62
+ end
63
+
64
+ after do
65
+ Paperclip::Attachment.default_options.merge!(@old_default_options)
66
+ end
67
+
68
+ it "deep merges when it is overridden" do
69
+ new_options = { convert_options: { thumb: "-thumbnailize" } }
70
+ attachment = Paperclip::Attachment.new(:name, :instance, new_options)
71
+
72
+ expect(Paperclip::Attachment.default_options.deep_merge(new_options)).to eq attachment.instance_variable_get("@options")
73
+ end
74
+ end
75
+
76
+ it "handles a boolean second argument to #url" do
77
+ mock_url_generator_builder = MockUrlGeneratorBuilder.new
78
+ attachment = Paperclip::Attachment.new(
79
+ :name,
80
+ FakeModel.new,
81
+ url_generator: mock_url_generator_builder
82
+ )
83
+
84
+ attachment.url(:style_name, true)
85
+ expect(mock_url_generator_builder.has_generated_url_with_options?(timestamp: true, escape: true)).to eq true
86
+
87
+ attachment.url(:style_name, false)
88
+ expect(mock_url_generator_builder.has_generated_url_with_options?(timestamp: false, escape: true)).to eq true
89
+ end
90
+
91
+ it "passes the style and options through to the URL generator on #url" do
92
+ mock_url_generator_builder = MockUrlGeneratorBuilder.new
93
+ attachment = Paperclip::Attachment.new(
94
+ :name,
95
+ FakeModel.new,
96
+ url_generator: mock_url_generator_builder
97
+ )
98
+
99
+ attachment.url(:style_name, options: :values)
100
+ expect(mock_url_generator_builder.has_generated_url_with_options?(options: :values)).to eq true
101
+ end
102
+
103
+ it "passes default options through when #url is given one argument" do
104
+ mock_url_generator_builder = MockUrlGeneratorBuilder.new
105
+ attachment = Paperclip::Attachment.new(:name,
106
+ FakeModel.new,
107
+ url_generator: mock_url_generator_builder,
108
+ use_timestamp: true)
109
+
110
+ attachment.url(:style_name)
111
+ assert mock_url_generator_builder.has_generated_url_with_options?(escape: true, timestamp: true)
112
+ end
113
+
114
+ it "passes default style and options through when #url is given no arguments" do
115
+ mock_url_generator_builder = MockUrlGeneratorBuilder.new
116
+ attachment = Paperclip::Attachment.new(:name,
117
+ FakeModel.new,
118
+ default_style: 'default style',
119
+ url_generator: mock_url_generator_builder,
120
+ use_timestamp: true)
121
+
122
+ attachment.url
123
+ assert mock_url_generator_builder.has_generated_url_with_options?(escape: true, timestamp: true)
124
+ assert mock_url_generator_builder.has_generated_url_with_style_name?('default style')
125
+ end
126
+
127
+ it "passes the option timestamp: true if :use_timestamp is true and :timestamp is not passed" do
128
+ mock_url_generator_builder = MockUrlGeneratorBuilder.new
129
+ attachment = Paperclip::Attachment.new(:name,
130
+ FakeModel.new,
131
+ url_generator: mock_url_generator_builder,
132
+ use_timestamp: true)
133
+
134
+ attachment.url(:style_name)
135
+ assert mock_url_generator_builder.has_generated_url_with_options?(escape: true, timestamp: true)
136
+ end
137
+
138
+ it "passes the option timestamp: false if :use_timestamp is false and :timestamp is not passed" do
139
+ mock_url_generator_builder = MockUrlGeneratorBuilder.new
140
+ attachment = Paperclip::Attachment.new(:name,
141
+ FakeModel.new,
142
+ url_generator: mock_url_generator_builder,
143
+ use_timestamp: false)
144
+
145
+ attachment.url(:style_name)
146
+ assert mock_url_generator_builder.has_generated_url_with_options?(escape: true, timestamp: false)
147
+ end
148
+
149
+ it "does not change the :timestamp if :timestamp is passed" do
150
+ mock_url_generator_builder = MockUrlGeneratorBuilder.new
151
+ attachment = Paperclip::Attachment.new(:name,
152
+ FakeModel.new,
153
+ url_generator: mock_url_generator_builder,
154
+ use_timestamp: false)
155
+
156
+ attachment.url(:style_name, timestamp: true)
157
+ assert mock_url_generator_builder.has_generated_url_with_options?(escape: true, timestamp: true)
158
+ end
159
+
160
+ it "renders JSON as default style" do
161
+ mock_url_generator_builder = MockUrlGeneratorBuilder.new
162
+ attachment = Paperclip::Attachment.new(:name,
163
+ FakeModel.new,
164
+ default_style: 'default style',
165
+ url_generator: mock_url_generator_builder)
166
+
167
+ attachment.as_json
168
+ assert mock_url_generator_builder.has_generated_url_with_style_name?('default style')
169
+ end
170
+
171
+ it "passes the option escape: true if :escape_url is true and :escape is not passed" do
172
+ mock_url_generator_builder = MockUrlGeneratorBuilder.new
173
+ attachment = Paperclip::Attachment.new(:name,
174
+ FakeModel.new,
175
+ url_generator: mock_url_generator_builder,
176
+ escape_url: true)
177
+
178
+ attachment.url(:style_name)
179
+ assert mock_url_generator_builder.has_generated_url_with_options?(escape: true)
180
+ end
181
+
182
+ it "passes the option escape: false if :escape_url is false and :escape is not passed" do
183
+ mock_url_generator_builder = MockUrlGeneratorBuilder.new
184
+ attachment = Paperclip::Attachment.new(:name,
185
+ FakeModel.new,
186
+ url_generator: mock_url_generator_builder,
187
+ escape_url: false)
188
+
189
+ attachment.url(:style_name)
190
+ assert mock_url_generator_builder.has_generated_url_with_options?(escape: false)
191
+ end
192
+
193
+ it "returns the path based on the url by default" do
194
+ @attachment = attachment url: "/:class/:id/:basename"
195
+ @model = @attachment.instance
196
+ @model.id = 1234
197
+ @model.avatar_file_name = "fake.jpg"
198
+ assert_equal "#{Rails.root}/public/fake_models/1234/fake", @attachment.path
199
+ end
200
+
201
+ it "defaults to a path that scales" do
202
+ avatar_attachment = attachment
203
+ model = avatar_attachment.instance
204
+ model.id = 1234
205
+ model.avatar_file_name = "fake.jpg"
206
+ expected_path = "#{Rails.root}/public/system/fake_models/avatars/000/001/234/original/fake.jpg"
207
+ assert_equal expected_path, avatar_attachment.path
208
+ end
209
+
210
+ it "renders JSON as the URL to the attachment" do
211
+ avatar_attachment = attachment
212
+ model = avatar_attachment.instance
213
+ model.id = 1234
214
+ model.avatar_file_name = "fake.jpg"
215
+ assert_equal attachment.url, attachment.as_json
216
+ end
217
+
218
+ it "renders JSON from the model when requested by :methods" do
219
+ rebuild_model
220
+ dummy = Dummy.new
221
+ dummy.id = 1234
222
+ dummy.avatar_file_name = "fake.jpg"
223
+ dummy.stubs(:new_record?).returns(false)
224
+ expected_string = '{"avatar":"/system/dummies/avatars/000/001/234/original/fake.jpg"}'
225
+ if ActiveRecord::Base.include_root_in_json # This is true by default in Rails 3, and false in 4
226
+ expected_string = %({"dummy":#{expected_string}})
227
+ end
228
+ # active_model pre-3.2 checks only by calling any? on it, thus it doesn't work if it is empty
229
+ assert_equal expected_string, dummy.to_json(only: [:dummy_key_for_old_active_model], methods: [:avatar])
230
+ end
231
+
232
+ context "Attachment default_options" do
233
+ before do
234
+ rebuild_model
235
+ @old_default_options = Paperclip::Attachment.default_options.dup
236
+ @new_default_options = @old_default_options.merge({
237
+ path: "argle/bargle",
238
+ url: "fooferon",
239
+ default_url: "not here.png"
240
+ })
241
+ end
242
+
243
+ after do
244
+ Paperclip::Attachment.default_options.merge! @old_default_options
245
+ end
246
+
247
+ it "is overrideable" do
248
+ Paperclip::Attachment.default_options.merge!(@new_default_options)
249
+ @new_default_options.keys.each do |key|
250
+ assert_equal @new_default_options[key],
251
+ Paperclip::Attachment.default_options[key]
252
+ end
253
+ end
254
+
255
+ context "without an Attachment" do
256
+ before do
257
+ rebuild_model default_url: "default.url"
258
+ @dummy = Dummy.new
259
+ end
260
+
261
+ it "returns false when asked exists?" do
262
+ assert !@dummy.avatar.exists?
263
+ end
264
+
265
+ it "#url returns the default_url" do
266
+ expect(@dummy.avatar.url).to eq "default.url"
267
+ end
268
+ end
269
+
270
+ context "on an Attachment" do
271
+ before do
272
+ @dummy = Dummy.new
273
+ @attachment = @dummy.avatar
274
+ end
275
+
276
+ Paperclip::Attachment.default_options.keys.each do |key|
277
+ it "is the default_options for #{key}" do
278
+ assert_equal @old_default_options[key],
279
+ @attachment.instance_variable_get("@options")[key],
280
+ key.to_s
281
+ end
282
+ end
283
+
284
+ context "when redefined" do
285
+ before do
286
+ Paperclip::Attachment.default_options.merge!(@new_default_options)
287
+ @dummy = Dummy.new
288
+ @attachment = @dummy.avatar
289
+ end
290
+
291
+ Paperclip::Attachment.default_options.keys.each do |key|
292
+ it "is the new default_options for #{key}" do
293
+ assert_equal @new_default_options[key],
294
+ @attachment.instance_variable_get("@options")[key],
295
+ key.to_s
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ context "An attachment with similarly named interpolations" do
303
+ before do
304
+ rebuild_model path: ":id.omg/:id-bbq/:idwhat/:id_partition.wtf"
305
+ @dummy = Dummy.new
306
+ @dummy.stubs(:id).returns(1024)
307
+ @file = File.new(fixture_file("5k.png"), 'rb')
308
+ @dummy.avatar = @file
309
+ end
310
+
311
+ after { @file.close }
312
+
313
+ it "makes sure that they are interpolated correctly" do
314
+ assert_equal "1024.omg/1024-bbq/1024what/000/001/024.wtf", @dummy.avatar.path
315
+ end
316
+ end
317
+
318
+ context "An attachment with :timestamp interpolations" do
319
+ before do
320
+ @file = StringIO.new("...")
321
+ @zone = 'UTC'
322
+ Time.stubs(:zone).returns(@zone)
323
+ @zone_default = 'Eastern Time (US & Canada)'
324
+ Time.stubs(:zone_default).returns(@zone_default)
325
+ end
326
+
327
+ context "using default time zone" do
328
+ before do
329
+ rebuild_model path: ":timestamp", use_default_time_zone: true
330
+ @dummy = Dummy.new
331
+ @dummy.avatar = @file
332
+ end
333
+
334
+ it "returns a time in the default zone" do
335
+ assert_equal @dummy.avatar_updated_at.in_time_zone(@zone_default).to_s, @dummy.avatar.path
336
+ end
337
+ end
338
+
339
+ context "using per-thread time zone" do
340
+ before do
341
+ rebuild_model path: ":timestamp", use_default_time_zone: false
342
+ @dummy = Dummy.new
343
+ @dummy.avatar = @file
344
+ end
345
+
346
+ it "returns a time in the per-thread zone" do
347
+ assert_equal @dummy.avatar_updated_at.in_time_zone(@zone).to_s, @dummy.avatar.path
348
+ end
349
+ end
350
+ end
351
+
352
+ context "An attachment with :hash interpolations" do
353
+ before do
354
+ @file = File.open(fixture_file("5k.png"))
355
+ end
356
+
357
+ after do
358
+ @file.close
359
+ end
360
+
361
+ it "raises if no secret is provided" do
362
+ rebuild_model path: ":hash"
363
+ @attachment = Dummy.new.avatar
364
+ @attachment.assign @file
365
+
366
+ assert_raises ArgumentError do
367
+ @attachment.path
368
+ end
369
+ end
370
+
371
+ context "when secret is set" do
372
+ before do
373
+ rebuild_model path: ":hash",
374
+ hash_secret: "w00t",
375
+ hash_data: ":class/:attachment/:style/:filename"
376
+ @attachment = Dummy.new.avatar
377
+ @attachment.assign @file
378
+ end
379
+
380
+ it "results in the correct interpolation" do
381
+ assert_equal "dummies/avatars/original/5k.png",
382
+ @attachment.send(:interpolate, @attachment.options[:hash_data])
383
+ assert_equal "dummies/avatars/thumb/5k.png",
384
+ @attachment.send(:interpolate, @attachment.options[:hash_data], :thumb)
385
+ end
386
+
387
+ it "results in a correct hash" do
388
+ assert_equal "0a59e9142bba11576de1d353d8747b1acad5ad34", @attachment.path
389
+ assert_equal "b39a062c1e62e85a6c785ed00cf3bebf5f850e2b", @attachment.path(:thumb)
390
+ end
391
+ end
392
+ end
393
+
394
+ context "An attachment with a :rails_env interpolation" do
395
+ before do
396
+ @rails_env = "blah"
397
+ @id = 1024
398
+ rebuild_model path: ":rails_env/:id.png"
399
+ @dummy = Dummy.new
400
+ @dummy.stubs(:id).returns(@id)
401
+ @file = StringIO.new(".")
402
+ @dummy.avatar = @file
403
+ Rails.stubs(:env).returns(@rails_env)
404
+ end
405
+
406
+ it "returns the proper path" do
407
+ assert_equal "#{@rails_env}/#{@id}.png", @dummy.avatar.path
408
+ end
409
+ end
410
+
411
+ context "An attachment with a default style and an extension interpolation" do
412
+ before do
413
+ rebuild_model path: ":basename.:extension",
414
+ styles: { default: ["100x100", :jpg] },
415
+ default_style: :default
416
+ @attachment = Dummy.new.avatar
417
+ @file = File.open(fixture_file("5k.png"))
418
+ @file.stubs(:original_filename).returns("file.png")
419
+ end
420
+ it "returns the right extension for the path" do
421
+ @attachment.assign(@file)
422
+ assert_equal "file.jpg", @attachment.path
423
+ end
424
+ end
425
+
426
+ context "An attachment with :convert_options" do
427
+ before do
428
+ rebuild_model styles: {
429
+ thumb: "100x100",
430
+ large: "400x400"
431
+ },
432
+ convert_options: {
433
+ all: "-do_stuff",
434
+ thumb: "-thumbnailize"
435
+ }
436
+ @dummy = Dummy.new
437
+ @dummy.avatar
438
+ end
439
+
440
+ it "reports the correct options when sent #extra_options_for(:thumb)" do
441
+ assert_equal "-thumbnailize -do_stuff", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
442
+ end
443
+
444
+ it "reports the correct options when sent #extra_options_for(:large)" do
445
+ assert_equal "-do_stuff", @dummy.avatar.send(:extra_options_for, :large)
446
+ end
447
+ end
448
+
449
+ context "An attachment with :source_file_options" do
450
+ before do
451
+ rebuild_model styles: {
452
+ thumb: "100x100",
453
+ large: "400x400"
454
+ },
455
+ source_file_options: {
456
+ all: "-density 400",
457
+ thumb: "-depth 8"
458
+ }
459
+ @dummy = Dummy.new
460
+ @dummy.avatar
461
+ end
462
+
463
+ it "reports the correct options when sent #extra_source_file_options_for(:thumb)" do
464
+ assert_equal "-depth 8 -density 400", @dummy.avatar.send(:extra_source_file_options_for, :thumb), @dummy.avatar.source_file_options.inspect
465
+ end
466
+
467
+ it "reports the correct options when sent #extra_source_file_options_for(:large)" do
468
+ assert_equal "-density 400", @dummy.avatar.send(:extra_source_file_options_for, :large)
469
+ end
470
+ end
471
+
472
+ context "An attachment with :only_process" do
473
+ before do
474
+ rebuild_model styles: {
475
+ thumb: "100x100",
476
+ large: "400x400"
477
+ },
478
+ only_process: [:thumb]
479
+ @file = StringIO.new("...")
480
+ @attachment = Dummy.new.avatar
481
+ end
482
+
483
+ it "only processes the provided style" do
484
+ @attachment.expects(:post_process).with(:thumb)
485
+ @attachment.expects(:post_process).with(:large).never
486
+ @attachment.assign(@file)
487
+ end
488
+ end
489
+
490
+ context "An attachment with :only_process that is a proc" do
491
+ before do
492
+ rebuild_model styles: {
493
+ thumb: "100x100",
494
+ large: "400x400"
495
+ },
496
+ only_process: lambda { |attachment| [:thumb] }
497
+
498
+ @file = StringIO.new("...")
499
+ @attachment = Dummy.new.avatar
500
+ end
501
+
502
+ it "only processes the provided style" do
503
+ @attachment.expects(:post_process).with(:thumb)
504
+ @attachment.expects(:post_process).with(:large).never
505
+ @attachment.assign(@file)
506
+ end
507
+ end
508
+
509
+ context "An attachment with :convert_options that is a proc" do
510
+ before do
511
+ rebuild_model styles: {
512
+ thumb: "100x100",
513
+ large: "400x400"
514
+ },
515
+ convert_options: {
516
+ all: lambda{|i| i.all },
517
+ thumb: lambda{|i| i.thumb }
518
+ }
519
+ Dummy.class_eval do
520
+ def all; "-all"; end
521
+ def thumb; "-thumb"; end
522
+ end
523
+ @dummy = Dummy.new
524
+ @dummy.avatar
525
+ end
526
+
527
+ it "reports the correct options when sent #extra_options_for(:thumb)" do
528
+ assert_equal "-thumb -all", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
529
+ end
530
+
531
+ it "reports the correct options when sent #extra_options_for(:large)" do
532
+ assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large)
533
+ end
534
+ end
535
+
536
+ context "An attachment with :path that is a proc" do
537
+ before do
538
+ rebuild_model path: lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
539
+
540
+ @file = File.new(fixture_file("5k.png"), 'rb')
541
+ @dummyA = Dummy.new(other: 'a')
542
+ @dummyA.avatar = @file
543
+ @dummyB = Dummy.new(other: 'b')
544
+ @dummyB.avatar = @file
545
+ end
546
+
547
+ after { @file.close }
548
+
549
+ it "returns correct path" do
550
+ assert_equal "path/a.png", @dummyA.avatar.path
551
+ assert_equal "path/b.png", @dummyB.avatar.path
552
+ end
553
+ end
554
+
555
+ context "An attachment with :styles that is a proc" do
556
+ before do
557
+ rebuild_model styles: lambda{ |attachment| {thumb: "50x50#", large: "400x400"} }
558
+
559
+ @attachment = Dummy.new.avatar
560
+ end
561
+
562
+ it "has the correct geometry" do
563
+ assert_equal "50x50#", @attachment.styles[:thumb][:geometry]
564
+ end
565
+ end
566
+
567
+ context "An attachment with conditional :styles that is a proc" do
568
+ before do
569
+ rebuild_model styles: lambda{ |attachment| attachment.instance.other == 'a' ? {thumb: "50x50#"} : {large: "400x400"} }
570
+
571
+ @dummy = Dummy.new(other: 'a')
572
+ end
573
+
574
+ it "has the correct styles for the assigned instance values" do
575
+ assert_equal "50x50#", @dummy.avatar.styles[:thumb][:geometry]
576
+ assert_nil @dummy.avatar.styles[:large]
577
+
578
+ @dummy.other = 'b'
579
+
580
+ assert_equal "400x400", @dummy.avatar.styles[:large][:geometry]
581
+ assert_nil @dummy.avatar.styles[:thumb]
582
+ end
583
+ end
584
+
585
+ geometry_specs = [
586
+ [ lambda{|z| "50x50#" }, :png ],
587
+ lambda{|z| "50x50#" },
588
+ { geometry: lambda{|z| "50x50#" } }
589
+ ]
590
+ geometry_specs.each do |geometry_spec|
591
+ context "An attachment geometry like #{geometry_spec}" do
592
+ before do
593
+ rebuild_model styles: { normal: geometry_spec }
594
+ @attachment = Dummy.new.avatar
595
+ end
596
+
597
+ context "when assigned" do
598
+ before do
599
+ @file = StringIO.new(".")
600
+ @attachment.assign(@file)
601
+ end
602
+
603
+ it "has the correct geometry" do
604
+ assert_equal "50x50#", @attachment.styles[:normal][:geometry]
605
+ end
606
+ end
607
+ end
608
+ end
609
+
610
+ context "An attachment with both 'normal' and hash-style styles" do
611
+ before do
612
+ rebuild_model styles: {
613
+ normal: ["50x50#", :png],
614
+ hash: { geometry: "50x50#", format: :png }
615
+ }
616
+ @dummy = Dummy.new
617
+ @attachment = @dummy.avatar
618
+ end
619
+
620
+ [:processors, :whiny, :convert_options, :geometry, :format].each do |field|
621
+ it "has the same #{field} field" do
622
+ assert_equal @attachment.styles[:normal][field], @attachment.styles[:hash][field]
623
+ end
624
+ end
625
+ end
626
+
627
+ context "An attachment with :processors that is a proc" do
628
+ before do
629
+ class Paperclip::Test < Paperclip::Processor; end
630
+ @file = StringIO.new("...")
631
+ Paperclip::Test.stubs(:make).returns(@file)
632
+
633
+ rebuild_model styles: { normal: '' }, processors: lambda { |a| [ :test ] }
634
+ @attachment = Dummy.new.avatar
635
+ end
636
+
637
+ context "when assigned" do
638
+ before do
639
+ @attachment.assign(StringIO.new("."))
640
+ end
641
+
642
+ it "has the correct processors" do
643
+ assert_equal [ :test ], @attachment.styles[:normal][:processors]
644
+ end
645
+ end
646
+ end
647
+
648
+ context "An attachment with erroring processor" do
649
+ before do
650
+ rebuild_model processor: [:thumbnail], styles: { small: '' }, whiny_thumbnails: true
651
+ @dummy = Dummy.new
652
+ @file = StringIO.new("...")
653
+ @file.stubs(:to_tempfile).returns(@file)
654
+ end
655
+
656
+ context "when error is meaningful for the end user" do
657
+ before do
658
+ Paperclip::Thumbnail.expects(:make).raises(
659
+ Paperclip::Errors::NotIdentifiedByImageMagickError,
660
+ "cannot be processed."
661
+ )
662
+ end
663
+
664
+ it "correctly forwards processing error message to the instance" do
665
+ @dummy.avatar = @file
666
+ @dummy.valid?
667
+ assert_contains(
668
+ @dummy.errors.full_messages,
669
+ "Avatar cannot be processed."
670
+ )
671
+ end
672
+ end
673
+
674
+ context "when error is intended for the developer" do
675
+ before do
676
+ Paperclip::Thumbnail.expects(:make).raises(
677
+ Paperclip::Errors::CommandNotFoundError
678
+ )
679
+ end
680
+
681
+ it "propagates the error" do
682
+ assert_raises(Paperclip::Errors::CommandNotFoundError) do
683
+ @dummy.avatar = @file
684
+ end
685
+ end
686
+ end
687
+ end
688
+
689
+ context "An attachment with multiple processors" do
690
+ before do
691
+ class Paperclip::Test < Paperclip::Processor; end
692
+ @style_params = { once: {one: 1, two: 2} }
693
+ rebuild_model processors: [:thumbnail, :test], styles: @style_params
694
+ @dummy = Dummy.new
695
+ @file = StringIO.new("...")
696
+ @file.stubs(:close)
697
+ Paperclip::Test.stubs(:make).returns(@file)
698
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
699
+ end
700
+
701
+ context "when assigned" do
702
+ it "calls #make on all specified processors" do
703
+ Paperclip::Thumbnail.stubs(:make).with(any_parameters).returns(@file)
704
+ Paperclip::Test.stubs(:make).with(any_parameters).returns(@file)
705
+
706
+ @dummy.avatar = @file
707
+
708
+ expect(Paperclip::Thumbnail).to have_received(:make)
709
+ expect(Paperclip::Test).to have_received(:make)
710
+ end
711
+
712
+ it "calls #make with the right parameters passed as second argument" do
713
+ expected_params = @style_params[:once].merge({
714
+ style: :once,
715
+ processors: [:thumbnail, :test],
716
+ whiny: true,
717
+ convert_options: "",
718
+ source_file_options: ""
719
+ })
720
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
721
+
722
+ @dummy.avatar = @file
723
+
724
+ expect(Paperclip::Thumbnail).to have_received(:make).with(anything, expected_params, anything)
725
+ end
726
+
727
+ it "calls #make with attachment passed as third argument" do
728
+ Paperclip::Test.expects(:make).returns(@file)
729
+
730
+ @dummy.avatar = @file
731
+
732
+ expect(Paperclip::Test).to have_received(:make).with(anything, anything, @dummy.avatar)
733
+ end
734
+ end
735
+ end
736
+
737
+ it "includes the filesystem module when loading the filesystem storage" do
738
+ rebuild_model storage: :filesystem
739
+ @dummy = Dummy.new
740
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
741
+ end
742
+
743
+ it "includes the filesystem module even if capitalization is wrong" do
744
+ rebuild_model storage: :FileSystem
745
+ @dummy = Dummy.new
746
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
747
+
748
+ rebuild_model storage: :Filesystem
749
+ @dummy = Dummy.new
750
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
751
+ end
752
+
753
+ it "converts underscored storage name to camelcase" do
754
+ rebuild_model storage: :not_here
755
+ @dummy = Dummy.new
756
+ exception = assert_raises(Paperclip::Errors::StorageMethodNotFound, /NotHere/) do
757
+ @dummy.avatar
758
+ end
759
+ end
760
+
761
+ it "raises an error if you try to include a storage module that doesn't exist" do
762
+ rebuild_model storage: :not_here
763
+ @dummy = Dummy.new
764
+ assert_raises(Paperclip::Errors::StorageMethodNotFound) do
765
+ @dummy.avatar
766
+ end
767
+ end
768
+
769
+ context "An attachment with styles but no processors defined" do
770
+ before do
771
+ rebuild_model processors: [], styles: {something: '1'}
772
+ @dummy = Dummy.new
773
+ @file = StringIO.new("...")
774
+ end
775
+ it "raises when assigned to" do
776
+ assert_raises(RuntimeError){ @dummy.avatar = @file }
777
+ end
778
+ end
779
+
780
+ context "An attachment without styles and with no processors defined" do
781
+ before do
782
+ rebuild_model processors: [], styles: {}
783
+ @dummy = Dummy.new
784
+ @file = StringIO.new("...")
785
+ end
786
+ it "does not raise when assigned to" do
787
+ @dummy.avatar = @file
788
+ end
789
+ end
790
+
791
+ context "Assigning an attachment with post_process hooks" do
792
+ before do
793
+ rebuild_class styles: { something: "100x100#" }
794
+ Dummy.class_eval do
795
+ before_avatar_post_process :do_before_avatar
796
+ after_avatar_post_process :do_after_avatar
797
+ before_post_process :do_before_all
798
+ after_post_process :do_after_all
799
+ def do_before_avatar; end
800
+ def do_after_avatar; end
801
+ def do_before_all; end
802
+ def do_after_all; end
803
+ end
804
+ @file = StringIO.new(".")
805
+ @file.stubs(:to_tempfile).returns(@file)
806
+ @dummy = Dummy.new
807
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
808
+ @attachment = @dummy.avatar
809
+ end
810
+
811
+ it "calls the defined callbacks when assigned" do
812
+ @dummy.expects(:do_before_avatar).with()
813
+ @dummy.expects(:do_after_avatar).with()
814
+ @dummy.expects(:do_before_all).with()
815
+ @dummy.expects(:do_after_all).with()
816
+ Paperclip::Thumbnail.expects(:make).returns(@file)
817
+ @dummy.avatar = @file
818
+ end
819
+
820
+ it "does not cancel the processing if a before_post_process returns nil" do
821
+ @dummy.expects(:do_before_avatar).with().returns(nil)
822
+ @dummy.expects(:do_after_avatar).with()
823
+ @dummy.expects(:do_before_all).with().returns(nil)
824
+ @dummy.expects(:do_after_all).with()
825
+ Paperclip::Thumbnail.expects(:make).returns(@file)
826
+ @dummy.avatar = @file
827
+ end
828
+
829
+ it "cancels the processing if a before_post_process returns false" do
830
+ @dummy.expects(:do_before_avatar).never
831
+ @dummy.expects(:do_after_avatar).never
832
+ @dummy.expects(:do_before_all).with().returns(false)
833
+ @dummy.expects(:do_after_all)
834
+ Paperclip::Thumbnail.expects(:make).never
835
+ @dummy.avatar = @file
836
+ end
837
+
838
+ it "cancels the processing if a before_avatar_post_process returns false" do
839
+ @dummy.expects(:do_before_avatar).with().returns(false)
840
+ @dummy.expects(:do_after_avatar)
841
+ @dummy.expects(:do_before_all).with().returns(true)
842
+ @dummy.expects(:do_after_all)
843
+ Paperclip::Thumbnail.expects(:make).never
844
+ @dummy.avatar = @file
845
+ end
846
+ end
847
+
848
+ context "Assigning an attachment" do
849
+ before do
850
+ rebuild_model styles: { something: "100x100#" }
851
+ @file = File.new(fixture_file("5k.png"), "rb")
852
+ @dummy = Dummy.new
853
+ @dummy.avatar = @file
854
+ end
855
+
856
+ it "strips whitespace from original_filename field" do
857
+ assert_equal "5k.png", @dummy.avatar.original_filename
858
+ end
859
+
860
+ it "strips whitespace from content_type field" do
861
+ assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
862
+ end
863
+ end
864
+
865
+ context "Assigning an attachment" do
866
+ before do
867
+ rebuild_model styles: { something: "100x100#" }
868
+ @file = File.new(fixture_file("5k.png"), "rb")
869
+ @dummy = Dummy.new
870
+ @dummy.avatar = @file
871
+ end
872
+
873
+ it "makes sure the content_type is a string" do
874
+ assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
875
+ end
876
+ end
877
+
878
+ context "Attachment with strange letters" do
879
+ before do
880
+ rebuild_model
881
+ @file = File.new(fixture_file("5k.png"), "rb")
882
+ @file.stubs(:original_filename).returns("sheep_say_bæ.png")
883
+ @dummy = Dummy.new
884
+ @dummy.avatar = @file
885
+ end
886
+
887
+ it "does not remove strange letters" do
888
+ assert_equal "sheep_say_bæ.png", @dummy.avatar.original_filename
889
+ end
890
+ end
891
+
892
+ context "Attachment with reserved filename" do
893
+ before do
894
+ rebuild_model
895
+ @file = Tempfile.new(["filename","png"])
896
+ end
897
+
898
+ after do
899
+ @file.unlink
900
+ end
901
+
902
+ context "with default configuration" do
903
+ "&$+,/:;=?@<>[]{}|\^~%# ".split(//).each do |character|
904
+ context "with character #{character}" do
905
+
906
+ context "at beginning of filename" do
907
+ before do
908
+ @file.stubs(:original_filename).returns("#{character}filename.png")
909
+ @dummy = Dummy.new
910
+ @dummy.avatar = @file
911
+ end
912
+
913
+ it "converts special character into underscore" do
914
+ assert_equal "_filename.png", @dummy.avatar.original_filename
915
+ end
916
+ end
917
+
918
+ context "at end of filename" do
919
+ before do
920
+ @file.stubs(:original_filename).returns("filename.png#{character}")
921
+ @dummy = Dummy.new
922
+ @dummy.avatar = @file
923
+ end
924
+
925
+ it "converts special character into underscore" do
926
+ assert_equal "filename.png_", @dummy.avatar.original_filename
927
+ end
928
+ end
929
+
930
+ context "in the middle of filename" do
931
+ before do
932
+ @file.stubs(:original_filename).returns("file#{character}name.png")
933
+ @dummy = Dummy.new
934
+ @dummy.avatar = @file
935
+ end
936
+
937
+ it "converts special character into underscore" do
938
+ assert_equal "file_name.png", @dummy.avatar.original_filename
939
+ end
940
+ end
941
+
942
+ end
943
+ end
944
+ end
945
+
946
+ context "with specified regexp replacement" do
947
+ before do
948
+ @old_defaults = Paperclip::Attachment.default_options.dup
949
+ end
950
+
951
+ after do
952
+ Paperclip::Attachment.default_options.merge! @old_defaults
953
+ end
954
+
955
+ context 'as another regexp' do
956
+ before do
957
+ Paperclip::Attachment.default_options.merge! restricted_characters: /o/
958
+
959
+ @file.stubs(:original_filename).returns("goood.png")
960
+ @dummy = Dummy.new
961
+ @dummy.avatar = @file
962
+ end
963
+
964
+ it "matches and converts that character" do
965
+ assert_equal "g___d.png", @dummy.avatar.original_filename
966
+ end
967
+ end
968
+
969
+ context 'as nil' do
970
+ before do
971
+ Paperclip::Attachment.default_options.merge! restricted_characters: nil
972
+
973
+ @file.stubs(:original_filename).returns("goood.png")
974
+ @dummy = Dummy.new
975
+ @dummy.avatar = @file
976
+ end
977
+
978
+ it "ignores and returns the original file name" do
979
+ assert_equal "goood.png", @dummy.avatar.original_filename
980
+ end
981
+ end
982
+ end
983
+
984
+ context 'with specified cleaner' do
985
+ before do
986
+ @old_defaults = Paperclip::Attachment.default_options.dup
987
+ end
988
+
989
+ after do
990
+ Paperclip::Attachment.default_options.merge! @old_defaults
991
+ end
992
+
993
+ it 'calls the given proc and take the result as cleaned filename' do
994
+ Paperclip::Attachment.default_options[:filename_cleaner] = lambda do |str|
995
+ "from_proc_#{str}"
996
+ end
997
+
998
+ @file.stubs(:original_filename).returns("goood.png")
999
+ @dummy = Dummy.new
1000
+ @dummy.avatar = @file
1001
+ assert_equal "from_proc_goood.png", @dummy.avatar.original_filename
1002
+ end
1003
+
1004
+ it 'calls the given object and take the result as the cleaned filename' do
1005
+ class MyCleaner
1006
+ def call(filename)
1007
+ "foo"
1008
+ end
1009
+ end
1010
+ Paperclip::Attachment.default_options[:filename_cleaner] = MyCleaner.new
1011
+
1012
+ @file.stubs(:original_filename).returns("goood.png")
1013
+ @dummy = Dummy.new
1014
+ @dummy.avatar = @file
1015
+ assert_equal "foo", @dummy.avatar.original_filename
1016
+ end
1017
+ end
1018
+ end
1019
+
1020
+ context "Attachment with uppercase extension and a default style" do
1021
+ before do
1022
+ @old_defaults = Paperclip::Attachment.default_options.dup
1023
+ Paperclip::Attachment.default_options.merge!({
1024
+ path: ":rails_root/:attachment/:class/:style/:id/:basename.:extension"
1025
+ })
1026
+ FileUtils.rm_rf("tmp")
1027
+ rebuild_model styles: { large: ["400x400", :jpg],
1028
+ medium: ["100x100", :jpg],
1029
+ small: ["32x32#", :jpg]},
1030
+ default_style: :small
1031
+ @instance = Dummy.new
1032
+ @instance.stubs(:id).returns 123
1033
+ @file = File.new(fixture_file("uppercase.PNG"), 'rb')
1034
+
1035
+ @attachment = @instance.avatar
1036
+
1037
+ now = Time.now
1038
+ Time.stubs(:now).returns(now)
1039
+ @attachment.assign(@file)
1040
+ @attachment.save
1041
+ end
1042
+
1043
+ after do
1044
+ @file.close
1045
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
1046
+ end
1047
+
1048
+ it "has matching to_s and url methods" do
1049
+ assert_equal @attachment.to_s, @attachment.url
1050
+ assert_equal @attachment.to_s(:small), @attachment.url(:small)
1051
+ end
1052
+
1053
+ it "has matching expiring_url and url methods when using the filesystem storage" do
1054
+ assert_equal @attachment.expiring_url, @attachment.url
1055
+ end
1056
+ end
1057
+
1058
+ context "An attachment" do
1059
+ before do
1060
+ @old_defaults = Paperclip::Attachment.default_options.dup
1061
+ Paperclip::Attachment.default_options.merge!({
1062
+ path: ":rails_root/:attachment/:class/:style/:id/:basename.:extension"
1063
+ })
1064
+ FileUtils.rm_rf("tmp")
1065
+ rebuild_model
1066
+ @instance = Dummy.new
1067
+ @instance.stubs(:id).returns 123
1068
+ # @attachment = Paperclip::Attachment.new(:avatar, @instance)
1069
+ @attachment = @instance.avatar
1070
+ @file = File.new(fixture_file("5k.png"), 'rb')
1071
+ end
1072
+
1073
+ after do
1074
+ @file.close
1075
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
1076
+ end
1077
+
1078
+ it "raises if there are not the correct columns when you try to assign" do
1079
+ @other_attachment = Paperclip::Attachment.new(:not_here, @instance)
1080
+ assert_raises(Paperclip::Error) do
1081
+ @other_attachment.assign(@file)
1082
+ end
1083
+ end
1084
+
1085
+ it 'clears out the previous assignment when assigned nil' do
1086
+ @attachment.assign(@file)
1087
+ @attachment.queued_for_write[:original]
1088
+ @attachment.assign(nil)
1089
+ assert_nil @attachment.queued_for_write[:original]
1090
+ end
1091
+
1092
+ it 'does not do anything when it is assigned an empty string' do
1093
+ @attachment.assign(@file)
1094
+ original_file = @attachment.queued_for_write[:original]
1095
+ @attachment.assign("")
1096
+ assert_equal original_file, @attachment.queued_for_write[:original]
1097
+ end
1098
+
1099
+ it "returns nil as path when no file assigned" do
1100
+ assert_equal nil, @attachment.path
1101
+ assert_equal nil, @attachment.path(:blah)
1102
+ end
1103
+
1104
+ context "with a file assigned but not saved yet" do
1105
+ it "clears out any attached files" do
1106
+ @attachment.assign(@file)
1107
+ assert !@attachment.queued_for_write.blank?
1108
+ @attachment.clear
1109
+ assert @attachment.queued_for_write.blank?
1110
+ end
1111
+ end
1112
+
1113
+ context "with a file assigned in the database" do
1114
+ before do
1115
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
1116
+ @attachment.stubs(:instance_read).with(:content_type).returns("image/png")
1117
+ @attachment.stubs(:instance_read).with(:file_size).returns(12345)
1118
+ dtnow = DateTime.now
1119
+ @now = Time.now
1120
+ Time.stubs(:now).returns(@now)
1121
+ @attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
1122
+ end
1123
+
1124
+ it "returns the proper path when filename has a single .'s" do
1125
+ assert_equal File.expand_path("tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
1126
+ end
1127
+
1128
+ it "returns the proper path when filename has multiple .'s" do
1129
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
1130
+ assert_equal File.expand_path("tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
1131
+ end
1132
+
1133
+ context "when expecting three styles" do
1134
+ before do
1135
+ rebuild_class styles: {
1136
+ large: ["400x400", :png],
1137
+ medium: ["100x100", :gif],
1138
+ small: ["32x32#", :jpg]
1139
+ }
1140
+ @instance = Dummy.new
1141
+ @instance.stubs(:id).returns 123
1142
+ @file = File.new(fixture_file("5k.png"), 'rb')
1143
+ @attachment = @instance.avatar
1144
+ end
1145
+
1146
+ context "and assigned a file" do
1147
+ before do
1148
+ now = Time.now
1149
+ Time.stubs(:now).returns(now)
1150
+ @attachment.assign(@file)
1151
+ end
1152
+
1153
+ it "is dirty" do
1154
+ assert @attachment.dirty?
1155
+ end
1156
+
1157
+ context "and saved" do
1158
+ before do
1159
+ @attachment.save
1160
+ end
1161
+
1162
+ it "commits the files to disk" do
1163
+ [:large, :medium, :small].each do |style|
1164
+ expect(@attachment.path(style)).to exist
1165
+ end
1166
+ end
1167
+
1168
+ it "saves the files as the right formats and sizes" do
1169
+ [[:large, 400, 61, "PNG"],
1170
+ [:medium, 100, 15, "GIF"],
1171
+ [:small, 32, 32, "JPEG"]].each do |style|
1172
+ cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"]
1173
+ out = `#{cmd}`
1174
+ width, height, _size, format = out.split(" ")
1175
+ assert_equal style[1].to_s, width.to_s
1176
+ assert_equal style[2].to_s, height.to_s
1177
+ assert_equal style[3].to_s, format.to_s
1178
+ end
1179
+ end
1180
+
1181
+ context "and trying to delete" do
1182
+ before do
1183
+ @existing_names = @attachment.styles.keys.collect do |style|
1184
+ @attachment.path(style)
1185
+ end
1186
+ end
1187
+
1188
+ it "deletes the files after assigning nil" do
1189
+ @attachment.expects(:instance_write).with(:file_name, nil)
1190
+ @attachment.expects(:instance_write).with(:content_type, nil)
1191
+ @attachment.expects(:instance_write).with(:file_size, nil)
1192
+ @attachment.expects(:instance_write).with(:fingerprint, nil)
1193
+ @attachment.expects(:instance_write).with(:updated_at, nil)
1194
+ @attachment.assign nil
1195
+ @attachment.save
1196
+ @existing_names.each{|f| assert_file_not_exists(f) }
1197
+ end
1198
+
1199
+ it "deletes the files when you call #clear and #save" do
1200
+ @attachment.expects(:instance_write).with(:file_name, nil)
1201
+ @attachment.expects(:instance_write).with(:content_type, nil)
1202
+ @attachment.expects(:instance_write).with(:file_size, nil)
1203
+ @attachment.expects(:instance_write).with(:fingerprint, nil)
1204
+ @attachment.expects(:instance_write).with(:updated_at, nil)
1205
+ @attachment.clear
1206
+ @attachment.save
1207
+ @existing_names.each{|f| assert_file_not_exists(f) }
1208
+ end
1209
+
1210
+ it "deletes the files when you call #delete" do
1211
+ @attachment.expects(:instance_write).with(:file_name, nil)
1212
+ @attachment.expects(:instance_write).with(:content_type, nil)
1213
+ @attachment.expects(:instance_write).with(:file_size, nil)
1214
+ @attachment.expects(:instance_write).with(:fingerprint, nil)
1215
+ @attachment.expects(:instance_write).with(:updated_at, nil)
1216
+ @attachment.destroy
1217
+ @existing_names.each{|f| assert_file_not_exists(f) }
1218
+ end
1219
+
1220
+ context "when keeping old files" do
1221
+ before do
1222
+ @attachment.options[:keep_old_files] = true
1223
+ end
1224
+
1225
+ it "keeps the files after assigning nil" do
1226
+ @attachment.expects(:instance_write).with(:file_name, nil)
1227
+ @attachment.expects(:instance_write).with(:content_type, nil)
1228
+ @attachment.expects(:instance_write).with(:file_size, nil)
1229
+ @attachment.expects(:instance_write).with(:fingerprint, nil)
1230
+ @attachment.expects(:instance_write).with(:updated_at, nil)
1231
+ @attachment.assign nil
1232
+ @attachment.save
1233
+ @existing_names.each{|f| assert_file_exists(f) }
1234
+ end
1235
+
1236
+ it "keeps the files when you call #clear and #save" do
1237
+ @attachment.expects(:instance_write).with(:file_name, nil)
1238
+ @attachment.expects(:instance_write).with(:content_type, nil)
1239
+ @attachment.expects(:instance_write).with(:file_size, nil)
1240
+ @attachment.expects(:instance_write).with(:fingerprint, nil)
1241
+ @attachment.expects(:instance_write).with(:updated_at, nil)
1242
+ @attachment.clear
1243
+ @attachment.save
1244
+ @existing_names.each{|f| assert_file_exists(f) }
1245
+ end
1246
+
1247
+ it "keeps the files when you call #delete" do
1248
+ @attachment.expects(:instance_write).with(:file_name, nil)
1249
+ @attachment.expects(:instance_write).with(:content_type, nil)
1250
+ @attachment.expects(:instance_write).with(:file_size, nil)
1251
+ @attachment.expects(:instance_write).with(:fingerprint, nil)
1252
+ @attachment.expects(:instance_write).with(:updated_at, nil)
1253
+ @attachment.destroy
1254
+ @existing_names.each{|f| assert_file_exists(f) }
1255
+ end
1256
+ end
1257
+ end
1258
+ end
1259
+ end
1260
+ end
1261
+ end
1262
+
1263
+ context "when trying a nonexistant storage type" do
1264
+ before do
1265
+ rebuild_model storage: :not_here
1266
+ end
1267
+
1268
+ it "is not able to find the module" do
1269
+ assert_raises(Paperclip::Errors::StorageMethodNotFound){ Dummy.new.avatar }
1270
+ end
1271
+ end
1272
+ end
1273
+
1274
+ context "An attachment with only a avatar_file_name column" do
1275
+ before do
1276
+ ActiveRecord::Base.connection.create_table :dummies, force: true do |table|
1277
+ table.column :avatar_file_name, :string
1278
+ end
1279
+ rebuild_class
1280
+ @dummy = Dummy.new
1281
+ @file = File.new(fixture_file("5k.png"), 'rb')
1282
+ end
1283
+
1284
+ after { @file.close }
1285
+
1286
+ it "does not error when assigned an attachment" do
1287
+ assert_nothing_raised { @dummy.avatar = @file }
1288
+ end
1289
+
1290
+ it "does not return the time when sent #avatar_updated_at" do
1291
+ @dummy.avatar = @file
1292
+ assert_nil @dummy.avatar.updated_at
1293
+ end
1294
+
1295
+ it "returns the right value when sent #avatar_file_size" do
1296
+ @dummy.avatar = @file
1297
+ assert_equal File.size(@file), @dummy.avatar.size
1298
+ end
1299
+
1300
+ context "and avatar_created_at column" do
1301
+ before do
1302
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_created_at, :timestamp
1303
+ rebuild_class
1304
+ @dummy = Dummy.new
1305
+ end
1306
+
1307
+ it "does not error when assigned an attachment" do
1308
+ assert_nothing_raised { @dummy.avatar = @file }
1309
+ end
1310
+
1311
+ it "returns the creation time when sent #avatar_created_at" do
1312
+ now = Time.now
1313
+ Time.stubs(:now).returns(now)
1314
+ @dummy.avatar = @file
1315
+ assert_equal now.to_i, @dummy.avatar.created_at
1316
+ end
1317
+
1318
+ it "returns the creation time when sent #avatar_created_at and the entry has been updated" do
1319
+ creation = 2.hours.ago
1320
+ now = Time.now
1321
+ Time.stubs(:now).returns(creation)
1322
+ @dummy.avatar = @file
1323
+ Time.stubs(:now).returns(now)
1324
+ @dummy.avatar = @file
1325
+ assert_equal creation.to_i, @dummy.avatar.created_at
1326
+ assert_not_equal now.to_i, @dummy.avatar.created_at
1327
+ end
1328
+
1329
+ it "sets changed? to true on attachment assignment" do
1330
+ @dummy.avatar = @file
1331
+ @dummy.save!
1332
+ @dummy.avatar = @file
1333
+ assert @dummy.changed?
1334
+ end
1335
+ end
1336
+
1337
+ context "and avatar_updated_at column" do
1338
+ before do
1339
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
1340
+ rebuild_class
1341
+ @dummy = Dummy.new
1342
+ end
1343
+
1344
+ it "does not error when assigned an attachment" do
1345
+ assert_nothing_raised { @dummy.avatar = @file }
1346
+ end
1347
+
1348
+ it "returns the right value when sent #avatar_updated_at" do
1349
+ now = Time.now
1350
+ Time.stubs(:now).returns(now)
1351
+ @dummy.avatar = @file
1352
+ assert_equal now.to_i, @dummy.avatar.updated_at
1353
+ end
1354
+ end
1355
+
1356
+ it "does not calculate fingerprint" do
1357
+ @dummy.avatar = @file
1358
+ assert_nil @dummy.avatar.fingerprint
1359
+ end
1360
+
1361
+ context "and avatar_content_type column" do
1362
+ before do
1363
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
1364
+ rebuild_class
1365
+ @dummy = Dummy.new
1366
+ end
1367
+
1368
+ it "does not error when assigned an attachment" do
1369
+ assert_nothing_raised { @dummy.avatar = @file }
1370
+ end
1371
+
1372
+ it "returns the right value when sent #avatar_content_type" do
1373
+ @dummy.avatar = @file
1374
+ assert_equal "image/png", @dummy.avatar.content_type
1375
+ end
1376
+ end
1377
+
1378
+ context "and avatar_file_size column" do
1379
+ before do
1380
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer
1381
+ rebuild_class
1382
+ @dummy = Dummy.new
1383
+ end
1384
+
1385
+ it "does not error when assigned an attachment" do
1386
+ assert_nothing_raised { @dummy.avatar = @file }
1387
+ end
1388
+
1389
+ it "returns the right value when sent #avatar_file_size" do
1390
+ @dummy.avatar = @file
1391
+ assert_equal File.size(@file), @dummy.avatar.size
1392
+ end
1393
+
1394
+ it "returns the right value when saved, reloaded, and sent #avatar_file_size" do
1395
+ @dummy.avatar = @file
1396
+ @dummy.save
1397
+ @dummy = Dummy.find(@dummy.id)
1398
+ assert_equal File.size(@file), @dummy.avatar.size
1399
+ end
1400
+ end
1401
+
1402
+ context "and avatar_fingerprint column" do
1403
+ before do
1404
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_fingerprint, :string
1405
+ rebuild_class
1406
+ @dummy = Dummy.new
1407
+ end
1408
+
1409
+ it "does not error when assigned an attachment" do
1410
+ assert_nothing_raised { @dummy.avatar = @file }
1411
+ end
1412
+
1413
+ it "returns the right value when sent #avatar_fingerprint" do
1414
+ @dummy.avatar = @file
1415
+ assert_equal 'aec488126c3b33c08a10c3fa303acf27', @dummy.avatar_fingerprint
1416
+ end
1417
+
1418
+ it "returns the right value when saved, reloaded, and sent #avatar_fingerprint" do
1419
+ @dummy.avatar = @file
1420
+ @dummy.save
1421
+ @dummy = Dummy.find(@dummy.id)
1422
+ assert_equal 'aec488126c3b33c08a10c3fa303acf27', @dummy.avatar_fingerprint
1423
+ end
1424
+ end
1425
+ end
1426
+
1427
+ context "an attachment with delete_file option set to false" do
1428
+ before do
1429
+ rebuild_model preserve_files: true
1430
+ @dummy = Dummy.new
1431
+ @file = File.new(fixture_file("5k.png"), 'rb')
1432
+ @dummy.avatar = @file
1433
+ @dummy.save!
1434
+ @attachment = @dummy.avatar
1435
+ @path = @attachment.path
1436
+ end
1437
+
1438
+ after { @file.close }
1439
+
1440
+ it "does not delete the files from storage when attachment is destroyed" do
1441
+ @attachment.destroy
1442
+ assert_file_exists(@path)
1443
+ end
1444
+
1445
+ it "clears out attachment data when attachment is destroyed" do
1446
+ @attachment.destroy
1447
+ assert !@attachment.exists?
1448
+ assert_nil @dummy.avatar_file_name
1449
+ end
1450
+
1451
+ it "does not delete the file when model is destroyed" do
1452
+ @dummy.destroy
1453
+ assert_file_exists(@path)
1454
+ end
1455
+ end
1456
+
1457
+ context "An attached file" do
1458
+ before do
1459
+ rebuild_model
1460
+ @dummy = Dummy.new
1461
+ @file = File.new(fixture_file("5k.png"), 'rb')
1462
+ @dummy.avatar = @file
1463
+ @dummy.save!
1464
+ @attachment = @dummy.avatar
1465
+ @path = @attachment.path
1466
+ end
1467
+
1468
+ after { @file.close }
1469
+
1470
+ it "is not deleted when the model fails to destroy" do
1471
+ @dummy.stubs(:destroy).raises(Exception)
1472
+
1473
+ assert_raises Exception do
1474
+ @dummy.destroy
1475
+ end
1476
+
1477
+ assert_file_exists(@path)
1478
+ end
1479
+
1480
+ it "is deleted when the model is destroyed" do
1481
+ @dummy.destroy
1482
+ assert_file_not_exists(@path)
1483
+ end
1484
+
1485
+ it "is not deleted when transaction rollbacks after model is destroyed" do
1486
+ ActiveRecord::Base.transaction do
1487
+ @dummy.destroy
1488
+ raise ActiveRecord::Rollback
1489
+ end
1490
+
1491
+ assert_file_exists(@path)
1492
+ end
1493
+ end
1494
+ end