jr-paperclip 7.3.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 (200) hide show
  1. checksums.yaml +7 -0
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  4. data/.github/ISSUE_TEMPLATE/custom.md +10 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. data/.github/workflows/reviewdog.yml +23 -0
  7. data/.github/workflows/test.yml +46 -0
  8. data/.gitignore +19 -0
  9. data/.qlty/.gitignore +7 -0
  10. data/.qlty/qlty.toml +89 -0
  11. data/.rubocop.yml +1060 -0
  12. data/Appraisals +29 -0
  13. data/CONTRIBUTING.md +85 -0
  14. data/Gemfile +17 -0
  15. data/LICENSE +25 -0
  16. data/NEWS +567 -0
  17. data/README.md +1083 -0
  18. data/RELEASING.md +17 -0
  19. data/Rakefile +52 -0
  20. data/bin/console +11 -0
  21. data/features/basic_integration.feature +85 -0
  22. data/features/migration.feature +29 -0
  23. data/features/rake_tasks.feature +62 -0
  24. data/features/step_definitions/attachment_steps.rb +121 -0
  25. data/features/step_definitions/html_steps.rb +15 -0
  26. data/features/step_definitions/rails_steps.rb +271 -0
  27. data/features/step_definitions/s3_steps.rb +16 -0
  28. data/features/step_definitions/web_steps.rb +106 -0
  29. data/features/support/env.rb +12 -0
  30. data/features/support/file_helpers.rb +34 -0
  31. data/features/support/fixtures/boot_config.txt +15 -0
  32. data/features/support/fixtures/gemfile.txt +5 -0
  33. data/features/support/fixtures/preinitializer.txt +20 -0
  34. data/features/support/paths.rb +28 -0
  35. data/features/support/rails.rb +39 -0
  36. data/features/support/selectors.rb +19 -0
  37. data/features/support/webmock_setup.rb +8 -0
  38. data/gemfiles/7.0.gemfile +20 -0
  39. data/gemfiles/7.1.gemfile +20 -0
  40. data/gemfiles/7.2.gemfile +20 -0
  41. data/gemfiles/8.0.gemfile +20 -0
  42. data/gemfiles/8.1.gemfile +20 -0
  43. data/lib/generators/paperclip/USAGE +8 -0
  44. data/lib/generators/paperclip/paperclip_generator.rb +36 -0
  45. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
  46. data/lib/jr-paperclip.rb +1 -0
  47. data/lib/paperclip/attachment.rb +634 -0
  48. data/lib/paperclip/attachment_registry.rb +60 -0
  49. data/lib/paperclip/callbacks.rb +42 -0
  50. data/lib/paperclip/content_type_detector.rb +85 -0
  51. data/lib/paperclip/errors.rb +34 -0
  52. data/lib/paperclip/file_command_content_type_detector.rb +28 -0
  53. data/lib/paperclip/filename_cleaner.rb +15 -0
  54. data/lib/paperclip/geometry.rb +157 -0
  55. data/lib/paperclip/geometry_detector_factory.rb +45 -0
  56. data/lib/paperclip/geometry_parser_factory.rb +31 -0
  57. data/lib/paperclip/glue.rb +18 -0
  58. data/lib/paperclip/has_attached_file.rb +116 -0
  59. data/lib/paperclip/helpers.rb +60 -0
  60. data/lib/paperclip/interpolations/plural_cache.rb +18 -0
  61. data/lib/paperclip/interpolations.rb +205 -0
  62. data/lib/paperclip/io_adapters/abstract_adapter.rb +75 -0
  63. data/lib/paperclip/io_adapters/attachment_adapter.rb +56 -0
  64. data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
  65. data/lib/paperclip/io_adapters/empty_string_adapter.rb +19 -0
  66. data/lib/paperclip/io_adapters/file_adapter.rb +26 -0
  67. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +16 -0
  68. data/lib/paperclip/io_adapters/identity_adapter.rb +17 -0
  69. data/lib/paperclip/io_adapters/nil_adapter.rb +37 -0
  70. data/lib/paperclip/io_adapters/registry.rb +36 -0
  71. data/lib/paperclip/io_adapters/stringio_adapter.rb +36 -0
  72. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +44 -0
  73. data/lib/paperclip/io_adapters/uri_adapter.rb +78 -0
  74. data/lib/paperclip/locales/en.yml +18 -0
  75. data/lib/paperclip/locales/fr.yml +18 -0
  76. data/lib/paperclip/locales/gd.yml +20 -0
  77. data/lib/paperclip/logger.rb +21 -0
  78. data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
  79. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +101 -0
  80. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
  81. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +97 -0
  82. data/lib/paperclip/matchers.rb +64 -0
  83. data/lib/paperclip/media_type_spoof_detector.rb +93 -0
  84. data/lib/paperclip/missing_attachment_styles.rb +84 -0
  85. data/lib/paperclip/processor.rb +56 -0
  86. data/lib/paperclip/processor_helpers.rb +52 -0
  87. data/lib/paperclip/rails_environment.rb +21 -0
  88. data/lib/paperclip/railtie.rb +31 -0
  89. data/lib/paperclip/schema.rb +104 -0
  90. data/lib/paperclip/storage/filesystem.rb +99 -0
  91. data/lib/paperclip/storage/fog.rb +262 -0
  92. data/lib/paperclip/storage/s3.rb +497 -0
  93. data/lib/paperclip/storage.rb +3 -0
  94. data/lib/paperclip/style.rb +106 -0
  95. data/lib/paperclip/tempfile.rb +42 -0
  96. data/lib/paperclip/tempfile_factory.rb +22 -0
  97. data/lib/paperclip/thumbnail.rb +131 -0
  98. data/lib/paperclip/url_generator.rb +83 -0
  99. data/lib/paperclip/validators/attachment_content_type_validator.rb +95 -0
  100. data/lib/paperclip/validators/attachment_file_name_validator.rb +82 -0
  101. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +28 -0
  102. data/lib/paperclip/validators/attachment_presence_validator.rb +28 -0
  103. data/lib/paperclip/validators/attachment_size_validator.rb +126 -0
  104. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +29 -0
  105. data/lib/paperclip/validators.rb +73 -0
  106. data/lib/paperclip/version.rb +3 -0
  107. data/lib/paperclip.rb +215 -0
  108. data/lib/tasks/paperclip.rake +140 -0
  109. data/paperclip.gemspec +51 -0
  110. data/shoulda_macros/paperclip.rb +134 -0
  111. data/spec/database.yml +4 -0
  112. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  113. data/spec/paperclip/attachment_processing_spec.rb +79 -0
  114. data/spec/paperclip/attachment_registry_spec.rb +158 -0
  115. data/spec/paperclip/attachment_spec.rb +1617 -0
  116. data/spec/paperclip/content_type_detector_spec.rb +58 -0
  117. data/spec/paperclip/file_command_content_type_detector_spec.rb +40 -0
  118. data/spec/paperclip/filename_cleaner_spec.rb +13 -0
  119. data/spec/paperclip/geometry_detector_spec.rb +47 -0
  120. data/spec/paperclip/geometry_parser_spec.rb +73 -0
  121. data/spec/paperclip/geometry_spec.rb +267 -0
  122. data/spec/paperclip/glue_spec.rb +63 -0
  123. data/spec/paperclip/has_attached_file_spec.rb +78 -0
  124. data/spec/paperclip/integration_spec.rb +702 -0
  125. data/spec/paperclip/interpolations_spec.rb +270 -0
  126. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +160 -0
  127. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +167 -0
  128. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +88 -0
  129. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  130. data/spec/paperclip/io_adapters/file_adapter_spec.rb +134 -0
  131. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +142 -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 +231 -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 +108 -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 +126 -0
  143. data/spec/paperclip/meta_class_spec.rb +30 -0
  144. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +88 -0
  145. data/spec/paperclip/paperclip_spec.rb +196 -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 +30 -0
  150. data/spec/paperclip/rake_spec.rb +103 -0
  151. data/spec/paperclip/schema_spec.rb +298 -0
  152. data/spec/paperclip/storage/filesystem_spec.rb +102 -0
  153. data/spec/paperclip/storage/fog_spec.rb +606 -0
  154. data/spec/paperclip/storage/s3_live_spec.rb +188 -0
  155. data/spec/paperclip/storage/s3_spec.rb +1974 -0
  156. data/spec/paperclip/style_spec.rb +251 -0
  157. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  158. data/spec/paperclip/tempfile_spec.rb +35 -0
  159. data/spec/paperclip/thumbnail_spec.rb +504 -0
  160. data/spec/paperclip/url_generator_spec.rb +231 -0
  161. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +410 -0
  162. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +249 -0
  163. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
  164. data/spec/paperclip/validators/attachment_size_validator_spec.rb +325 -0
  165. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +48 -0
  166. data/spec/paperclip/validators_spec.rb +179 -0
  167. data/spec/spec_helper.rb +52 -0
  168. data/spec/support/assertions.rb +84 -0
  169. data/spec/support/fake_model.rb +24 -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/aws_s3.yml +13 -0
  178. data/spec/support/fixtures/bad.png +1 -0
  179. data/spec/support/fixtures/empty.html +1 -0
  180. data/spec/support/fixtures/empty.xlsx +0 -0
  181. data/spec/support/fixtures/fog.yml +8 -0
  182. data/spec/support/fixtures/rotated.jpg +0 -0
  183. data/spec/support/fixtures/s3.yml +8 -0
  184. data/spec/support/fixtures/sample.xlsm +0 -0
  185. data/spec/support/fixtures/spaced file.jpg +0 -0
  186. data/spec/support/fixtures/spaced file.png +0 -0
  187. data/spec/support/fixtures/text.txt +1 -0
  188. data/spec/support/fixtures/twopage.pdf +0 -0
  189. data/spec/support/fixtures/uppercase.PNG +0 -0
  190. data/spec/support/matchers/accept.rb +5 -0
  191. data/spec/support/matchers/exist.rb +5 -0
  192. data/spec/support/matchers/have_column.rb +23 -0
  193. data/spec/support/mock_attachment.rb +24 -0
  194. data/spec/support/mock_interpolator.rb +24 -0
  195. data/spec/support/mock_url_generator_builder.rb +26 -0
  196. data/spec/support/model_reconstruction.rb +72 -0
  197. data/spec/support/reporting.rb +11 -0
  198. data/spec/support/test_data.rb +13 -0
  199. data/spec/support/version_helper.rb +9 -0
  200. metadata +702 -0
@@ -0,0 +1,606 @@
1
+ require "spec_helper"
2
+ require "fog/aws"
3
+ require "fog/local"
4
+ require "timecop"
5
+
6
+ describe Paperclip::Storage::Fog do
7
+ context "" do
8
+ before { Fog.mock! }
9
+
10
+ context "deleting attachment styles" do
11
+ before do
12
+ rebuild_model styles: { medium: "300x300>", thumb: "100x100>" },
13
+ storage: :fog,
14
+ url: "/:attachment/:style/:filename",
15
+ fog_directory: "paperclip",
16
+ fog_credentials: fixture_file("fog.yml")
17
+ @file = File.open(fixture_file("5k.png"))
18
+ @dummy = Dummy.new
19
+ @dummy.avatar = @file
20
+ @dummy.save
21
+ end
22
+
23
+ after do
24
+ @file.close
25
+ FileUtils.rm_rf("tmp")
26
+ end
27
+
28
+ it "only issues a delete call once for each unique attachment style when nullifying attachment" do
29
+ @dummy.avatar.clear(:thumb)
30
+ @dummy.avatar = nil
31
+ assert_equal 4, @dummy.avatar.queued_for_delete.size
32
+
33
+ original = double("original")
34
+ medium = double("medium")
35
+ thumb = double("thumb")
36
+
37
+ allow(Fog::AWS::Storage::File).to receive(:new).and_return(original, medium, thumb)
38
+
39
+ expect(original).to receive(:destroy).once
40
+ expect(medium).to receive(:destroy).once
41
+ expect(thumb).to receive(:destroy).once
42
+ @dummy.save
43
+ end
44
+
45
+ it "only issues a delete call once for each unique attachment style when destroying model" do
46
+ @dummy.avatar.clear(:thumb)
47
+ assert_equal 1, @dummy.avatar.queued_for_delete.size
48
+
49
+ file = double("file")
50
+ allow(Fog::AWS::Storage::File).to receive(:new).and_return(file)
51
+ expect(file).to receive(:destroy).exactly(3).times
52
+ @dummy.destroy
53
+ end
54
+ end
55
+
56
+ context "with credentials provided in a path string" do
57
+ before do
58
+ rebuild_model styles: { medium: "300x300>", thumb: "100x100>" },
59
+ storage: :fog,
60
+ url: "/:attachment/:filename",
61
+ fog_directory: "paperclip",
62
+ fog_credentials: fixture_file("fog.yml")
63
+ @file = File.new(fixture_file("5k.png"), "rb")
64
+ @dummy = Dummy.new
65
+ @dummy.avatar = @file
66
+ end
67
+
68
+ after { @file.close }
69
+
70
+ it "has the proper information loading credentials from a file" do
71
+ assert_equal @dummy.avatar.fog_credentials[:provider], "AWS"
72
+ end
73
+ end
74
+
75
+ context "with credentials provided in a File object" do
76
+ before do
77
+ rebuild_model styles: { medium: "300x300>", thumb: "100x100>" },
78
+ storage: :fog,
79
+ url: "/:attachment/:filename",
80
+ fog_directory: "paperclip",
81
+ fog_credentials: File.open(fixture_file("fog.yml"))
82
+ @file = File.new(fixture_file("5k.png"), "rb")
83
+ @dummy = Dummy.new
84
+ @dummy.avatar = @file
85
+ end
86
+
87
+ after { @file.close }
88
+
89
+ it "has the proper information loading credentials from a file" do
90
+ assert_equal @dummy.avatar.fog_credentials[:provider], "AWS"
91
+ end
92
+ end
93
+
94
+ context "with default values for path and url" do
95
+ before do
96
+ rebuild_model styles: { medium: "300x300>", thumb: "100x100>" },
97
+ storage: :fog,
98
+ url: "/:attachment/:filename",
99
+ fog_directory: "paperclip",
100
+ fog_credentials: {
101
+ provider: "AWS",
102
+ aws_access_key_id: "AWS_ID",
103
+ aws_secret_access_key: "AWS_SECRET"
104
+ }
105
+ @file = File.new(fixture_file("5k.png"), "rb")
106
+ @dummy = Dummy.new
107
+ @dummy.avatar = @file
108
+ end
109
+
110
+ after { @file.close }
111
+
112
+ it "is able to interpolate the path without blowing up" do
113
+ assert_equal File.expand_path(File.join(File.dirname(__FILE__), "../../../tmp/public/avatars/5k.png")),
114
+ @dummy.avatar.path
115
+ end
116
+ end
117
+
118
+ context "with no path or url given and using defaults" do
119
+ before do
120
+ rebuild_model styles: { medium: "300x300>", thumb: "100x100>" },
121
+ storage: :fog,
122
+ fog_directory: "paperclip",
123
+ fog_credentials: {
124
+ provider: "AWS",
125
+ aws_access_key_id: "AWS_ID",
126
+ aws_secret_access_key: "AWS_SECRET"
127
+ }
128
+ @file = File.new(fixture_file("5k.png"), "rb")
129
+ @dummy = Dummy.new
130
+ @dummy.id = 1
131
+ @dummy.avatar = @file
132
+ end
133
+
134
+ after { @file.close }
135
+
136
+ it "has correct path and url from interpolated defaults" do
137
+ assert_equal "dummies/avatars/000/000/001/original/5k.png", @dummy.avatar.path
138
+ end
139
+ end
140
+
141
+ context "with file params provided as lambda" do
142
+ before do
143
+ fog_file = lambda { |a| { custom_header: a.instance.custom_method } }
144
+ klass = rebuild_model storage: :fog,
145
+ fog_file: fog_file
146
+
147
+ klass.class_eval do
148
+ def custom_method
149
+ "foobar"
150
+ end
151
+ end
152
+
153
+ @dummy = Dummy.new
154
+ end
155
+
156
+ it "is able to evaluate correct values for file headers" do
157
+ assert_equal @dummy.avatar.send(:fog_file), custom_header: "foobar"
158
+ end
159
+ end
160
+
161
+ before do
162
+ @fog_directory = "papercliptests"
163
+
164
+ @credentials = {
165
+ provider: "AWS",
166
+ aws_access_key_id: "ID",
167
+ aws_secret_access_key: "SECRET"
168
+ }
169
+
170
+ @connection = Fog::Storage.new(@credentials)
171
+ @connection.directories.create(
172
+ key: @fog_directory
173
+ )
174
+
175
+ @options = {
176
+ fog_directory: @fog_directory,
177
+ fog_credentials: @credentials,
178
+ fog_host: nil,
179
+ fog_file: { cache_control: 1234 },
180
+ path: ":attachment/:basename:dotextension",
181
+ storage: :fog
182
+ }
183
+
184
+ rebuild_model(@options)
185
+ end
186
+
187
+ it "is extended by the Fog module" do
188
+ assert Dummy.new.avatar.is_a?(Paperclip::Storage::Fog)
189
+ end
190
+
191
+ context "when assigned" do
192
+ before do
193
+ @file = File.new(fixture_file("5k.png"), "rb")
194
+ @dummy = Dummy.new
195
+ @dummy.avatar = @file
196
+ end
197
+
198
+ after do
199
+ @file.close
200
+ directory = @connection.directories.new(key: @fog_directory)
201
+ directory.files.each(&:destroy)
202
+ directory.destroy
203
+ end
204
+
205
+ it "is rewound after flush_writes" do
206
+ @dummy.avatar.instance_eval "def after_flush_writes; end"
207
+
208
+ files = @dummy.avatar.queued_for_write.values
209
+ @dummy.save
210
+ assert files.none?(&:eof?), "Expect all the files to be rewinded."
211
+ end
212
+
213
+ it "is removed after after_flush_writes" do
214
+ paths = @dummy.avatar.queued_for_write.values.map(&:path)
215
+ @dummy.save
216
+ assert paths.none? { |path| File.exist?(path) },
217
+ "Expect all the files to be deleted."
218
+ end
219
+
220
+ it "is able to be copied to a local file" do
221
+ @dummy.save
222
+ tempfile = Tempfile.new("known_location")
223
+ tempfile.binmode
224
+ @dummy.avatar.copy_to_local_file(:original, tempfile.path)
225
+ tempfile.rewind
226
+ assert_equal @connection.directories.get(@fog_directory).files.get(@dummy.avatar.path).body,
227
+ tempfile.read
228
+ tempfile.close
229
+ end
230
+
231
+ it "is able to be handled when missing while copying to a local file" do
232
+ tempfile = Tempfile.new("known_location")
233
+ tempfile.binmode
234
+ assert_equal false, @dummy.avatar.copy_to_local_file(:original, tempfile.path)
235
+ tempfile.close
236
+ end
237
+
238
+ it "passes the content type to the Fog::Storage::AWS::Files instance" do
239
+ expect_any_instance_of(Fog::Storage::AWS::Files).to receive(:create).with(hash_including(:content_type))
240
+ @dummy.save
241
+ end
242
+
243
+ context "without a bucket" do
244
+ before do
245
+ @connection.directories.get(@fog_directory).destroy
246
+ end
247
+
248
+ it "creates the bucket" do
249
+ assert @dummy.save
250
+ assert @connection.directories.get(@fog_directory)
251
+ end
252
+
253
+ it "sucessfully rewinds the file during bucket creation" do
254
+ assert @dummy.save
255
+ expect(Paperclip.io_adapters.for(@dummy.avatar).read.length).to be > 0
256
+ end
257
+ end
258
+
259
+ context "with a bucket" do
260
+ it "succeeds" do
261
+ assert @dummy.save
262
+ end
263
+ end
264
+
265
+ context "without a fog_host" do
266
+ before do
267
+ rebuild_model(@options.merge(fog_host: nil))
268
+ @dummy = Dummy.new
269
+ @dummy.avatar = StringIO.new(".")
270
+ @dummy.save
271
+ end
272
+
273
+ it "provides a public url" do
274
+ assert !@dummy.avatar.url.nil?
275
+ end
276
+ end
277
+
278
+ context "with a fog_host" do
279
+ before do
280
+ rebuild_model(@options.merge(fog_host: "http://example.com"))
281
+ @dummy = Dummy.new
282
+ @dummy.avatar = StringIO.new(".\n")
283
+ @dummy.save
284
+ end
285
+
286
+ it "provides a public url" do
287
+ expect(@dummy.avatar.url).to match(/^http:\/\/example\.com\/avatars\/data\?\d*$/)
288
+ end
289
+ end
290
+
291
+ context "with a fog_host that includes a wildcard placeholder" do
292
+ before do
293
+ rebuild_model(
294
+ fog_directory: @fog_directory,
295
+ fog_credentials: @credentials,
296
+ fog_host: "http://img%d.example.com",
297
+ path: ":attachment/:basename:dotextension",
298
+ storage: :fog
299
+ )
300
+ @dummy = Dummy.new
301
+ @dummy.avatar = StringIO.new(".\n")
302
+ @dummy.save
303
+ end
304
+
305
+ it "provides a public url" do
306
+ expect(@dummy.avatar.url).to match(/^http:\/\/img[0123]\.example\.com\/avatars\/data\?\d*$/)
307
+ end
308
+ end
309
+
310
+ context "with fog_public set to false" do
311
+ before do
312
+ rebuild_model(@options.merge(fog_public: false))
313
+ @dummy = Dummy.new
314
+ @dummy.avatar = StringIO.new(".")
315
+ @dummy.save
316
+ end
317
+
318
+ it "sets the @fog_public instance variable to false" do
319
+ assert_equal false, @dummy.avatar.instance_variable_get("@options")[:fog_public]
320
+ assert_equal false, @dummy.avatar.fog_public
321
+ end
322
+ end
323
+
324
+ context "with fog_public as a proc" do
325
+ let(:proc) { ->(attachment) { !attachment } }
326
+
327
+ before do
328
+ rebuild_model(@options.merge(fog_public: proc))
329
+ @dummy = Dummy.new
330
+ @dummy.avatar = StringIO.new(".")
331
+ @dummy.save
332
+ end
333
+
334
+ it "sets the @fog_public instance variable to false" do
335
+ assert_equal proc, @dummy.avatar.instance_variable_get("@options")[:fog_public]
336
+ assert_equal false, @dummy.avatar.fog_public
337
+ end
338
+ end
339
+
340
+ context "with styles set and fog_public set to false" do
341
+ before do
342
+ rebuild_model(@options.merge(fog_public: false, styles: { medium: "300x300>", thumb: "100x100>" }))
343
+ @file = File.new(fixture_file("5k.png"), "rb")
344
+ @dummy = Dummy.new
345
+ @dummy.avatar = @file
346
+ @dummy.save
347
+ end
348
+
349
+ it "sets the @fog_public for a particular style to false" do
350
+ assert_equal false, @dummy.avatar.instance_variable_get("@options")[:fog_public]
351
+ assert_equal false, @dummy.avatar.fog_public(:thumb)
352
+ end
353
+ end
354
+
355
+ context "with styles set and fog_public set per-style" do
356
+ before do
357
+ rebuild_model(@options.merge(fog_public: { medium: false, thumb: true },
358
+ styles: { medium: "300x300>", thumb: "100x100>" }))
359
+ @file = File.new(fixture_file("5k.png"), "rb")
360
+ @dummy = Dummy.new
361
+ @dummy.avatar = @file
362
+ @dummy.save
363
+ end
364
+
365
+ it "sets the fog_public for a particular style to correct value" do
366
+ assert_equal false, @dummy.avatar.fog_public(:medium)
367
+ assert_equal true, @dummy.avatar.fog_public(:thumb)
368
+ end
369
+ end
370
+
371
+ context "with fog_public not set" do
372
+ before do
373
+ rebuild_model(@options)
374
+ @dummy = Dummy.new
375
+ @dummy.avatar = StringIO.new(".")
376
+ @dummy.save
377
+ end
378
+
379
+ it "defaults fog_public to true" do
380
+ assert_equal true, @dummy.avatar.fog_public
381
+ end
382
+ end
383
+
384
+ context "with scheme set" do
385
+ before do
386
+ rebuild_model(@options.merge(fog_credentials: @credentials.merge(scheme: "http")))
387
+ @file = File.new(fixture_file("5k.png"), "rb")
388
+ @dummy = Dummy.new
389
+ @dummy.avatar = @file
390
+ @dummy.save
391
+ end
392
+
393
+ it "honors the scheme in public url" do
394
+ assert_match(/^http:\/\//, @dummy.avatar.url)
395
+ end
396
+ it "honors the scheme in expiring url" do
397
+ assert_match(/^http:\/\//, @dummy.avatar.expiring_url)
398
+ end
399
+ end
400
+
401
+ context "with scheme not set" do
402
+ before do
403
+ rebuild_model(@options)
404
+ @file = File.new(fixture_file("5k.png"), "rb")
405
+ @dummy = Dummy.new
406
+ @dummy.avatar = @file
407
+ @dummy.save
408
+ end
409
+
410
+ it "provides HTTPS public url" do
411
+ assert_match(/^https:\/\//, @dummy.avatar.url)
412
+ end
413
+ it "provides HTTPS expiring url" do
414
+ assert_match(/^https:\/\//, @dummy.avatar.expiring_url)
415
+ end
416
+ end
417
+
418
+ context "with a valid bucket name for a subdomain" do
419
+ before { allow(@dummy).to receive(:new_record?).and_return(false) }
420
+
421
+ it "provides an url in subdomain style" do
422
+ assert_match(/^https:\/\/papercliptests.s3.amazonaws.com\/avatars\/5k.png/, @dummy.avatar.url)
423
+ end
424
+
425
+ it "provides an url that expires in subdomain style" do
426
+ assert_match(/^https:\/\/papercliptests.s3.amazonaws.com\/avatars\/5k.png.+Expires=.+$/, @dummy.avatar.expiring_url)
427
+ end
428
+ end
429
+
430
+ context "generating an expiring url" do
431
+ it "generates the same url when using Times and Integer offsets" do
432
+ Timecop.freeze do
433
+ offset = 1234
434
+ rebuild_model(@options)
435
+ dummy = Dummy.new
436
+ dummy.avatar = StringIO.new(".")
437
+
438
+ assert_equal dummy.avatar.expiring_url(offset),
439
+ dummy.avatar.expiring_url(Time.now + offset)
440
+ end
441
+ end
442
+
443
+ it "matches the default url if there is no assignment" do
444
+ dummy = Dummy.new
445
+ assert_equal dummy.avatar.url, dummy.avatar.expiring_url
446
+ end
447
+
448
+ it "matches the default url when given a style if there is no assignment" do
449
+ dummy = Dummy.new
450
+ assert_equal dummy.avatar.url(:thumb), dummy.avatar.expiring_url(3600, :thumb)
451
+ end
452
+ end
453
+
454
+ context "with an invalid bucket name for a subdomain" do
455
+ before do
456
+ rebuild_model(@options.merge(fog_directory: "this_is_invalid"))
457
+ @dummy = Dummy.new
458
+ @dummy.avatar = @file
459
+ @dummy.save
460
+ end
461
+
462
+ it "does not match the bucket-subdomain restrictions" do
463
+ invalid_subdomains = %w(this_is_invalid in iamareallylongbucketnameiamareallylongbucketnameiamareallylongbu invalid- inval..id inval-.id inval.-id -invalid 192.168.10.2)
464
+ invalid_subdomains.each do |name|
465
+ assert_no_match Paperclip::Storage::Fog::AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX, name
466
+ end
467
+ end
468
+
469
+ it "provides an url in folder style" do
470
+ assert_match(/^https:\/\/s3.amazonaws.com\/this_is_invalid\/avatars\/5k.png\?\d*$/, @dummy.avatar.url)
471
+ end
472
+
473
+ it "provides a url that expires in folder style" do
474
+ assert_match(/^https:\/\/s3.amazonaws.com\/this_is_invalid\/avatars\/5k.png.+Expires=.+$/, @dummy.avatar.expiring_url)
475
+ end
476
+ end
477
+
478
+ context "with a proc for a bucket name evaluating a model method" do
479
+ before do
480
+ @dynamic_fog_directory = "dynamicpaperclip"
481
+ rebuild_model(@options.merge(fog_directory: lambda { |attachment| attachment.instance.bucket_name }))
482
+ @dummy = Dummy.new
483
+ allow(@dummy).to receive(:bucket_name).and_return(@dynamic_fog_directory)
484
+ @dummy.avatar = @file
485
+ @dummy.save
486
+ end
487
+
488
+ it "has created the bucket" do
489
+ assert @connection.directories.get(@dynamic_fog_directory).inspect
490
+ end
491
+
492
+ it "provides an url using dynamic bucket name" do
493
+ assert_match(/^https:\/\/dynamicpaperclip.s3.amazonaws.com\/avatars\/5k.png\?\d*$/, @dummy.avatar.url)
494
+ end
495
+ end
496
+
497
+ context "with a proc for the fog_host evaluating a model method" do
498
+ before do
499
+ rebuild_model(@options.merge(fog_host: lambda { |attachment| attachment.instance.fog_host }))
500
+ @dummy = Dummy.new
501
+ allow(@dummy).to receive(:fog_host).and_return("http://dynamicfoghost.com")
502
+ @dummy.avatar = @file
503
+ @dummy.save
504
+ end
505
+
506
+ it "provides a public url" do
507
+ assert_match(/http:\/\/dynamicfoghost\.com/, @dummy.avatar.url)
508
+ end
509
+ end
510
+
511
+ context "with a custom fog_host" do
512
+ before do
513
+ rebuild_model(@options.merge(fog_host: "http://dynamicfoghost.com"))
514
+ @dummy = Dummy.new
515
+ @dummy.avatar = @file
516
+ @dummy.save
517
+ end
518
+
519
+ it "provides a public url" do
520
+ assert_match(/http:\/\/dynamicfoghost\.com/, @dummy.avatar.url)
521
+ end
522
+
523
+ it "provides an expiring url" do
524
+ assert_match(/http:\/\/dynamicfoghost\.com/, @dummy.avatar.expiring_url)
525
+ end
526
+
527
+ context "with an invalid bucket name for a subdomain" do
528
+ before do
529
+ rebuild_model(@options.merge(fog_directory: "this_is_invalid", fog_host: "http://dynamicfoghost.com"))
530
+ @dummy = Dummy.new
531
+ @dummy.avatar = @file
532
+ @dummy.save
533
+ end
534
+
535
+ it "provides an expiring url" do
536
+ assert_match(/http:\/\/dynamicfoghost\.com/, @dummy.avatar.expiring_url)
537
+ end
538
+ end
539
+ end
540
+
541
+ context "with a proc for the fog_credentials evaluating a model method" do
542
+ before do
543
+ @dynamic_fog_credentials = {
544
+ provider: "AWS",
545
+ aws_access_key_id: "DYNAMIC_ID",
546
+ aws_secret_access_key: "DYNAMIC_SECRET"
547
+ }
548
+ rebuild_model(@options.merge(fog_credentials: lambda { |attachment| attachment.instance.fog_credentials }))
549
+ @dummy = Dummy.new
550
+ allow(@dummy).to receive(:fog_credentials).and_return(@dynamic_fog_credentials)
551
+ @dummy.avatar = @file
552
+ @dummy.save
553
+ end
554
+
555
+ it "provides a public url" do
556
+ assert_equal @dummy.avatar.fog_credentials, @dynamic_fog_credentials
557
+ end
558
+ end
559
+
560
+ context "with custom fog_options" do
561
+ before do
562
+ rebuild_model(
563
+ @options.merge(fog_options: { multipart_chunk_size: 104857600 })
564
+ )
565
+ @dummy = Dummy.new
566
+ @dummy.avatar = @file
567
+ end
568
+
569
+ it "applies the options to the fog #create call" do
570
+ files = double
571
+ allow(@dummy.avatar).to receive(:directory).and_return double(files: files)
572
+ expect(files).to receive(:create).with(
573
+ hash_including(multipart_chunk_size: 104857600)
574
+ )
575
+ @dummy.save
576
+ end
577
+ end
578
+ end
579
+ end
580
+
581
+ context "when using local storage" do
582
+ before do
583
+ Fog.unmock!
584
+ rebuild_model styles: { medium: "300x300>", thumb: "100x100>" },
585
+ storage: :fog,
586
+ url: "/:attachment/:filename",
587
+ fog_directory: "paperclip",
588
+ fog_credentials: { provider: :local, local_root: "." },
589
+ fog_host: "localhost"
590
+
591
+ @file = File.new(fixture_file("5k.png"), "rb")
592
+ @dummy = Dummy.new
593
+ @dummy.avatar = @file
594
+ allow(@dummy).to receive(:new_record?).and_return(false)
595
+ end
596
+
597
+ after do
598
+ @file.close
599
+ Fog.mock!
600
+ end
601
+
602
+ it "returns the public url in place of the expiring url" do
603
+ assert_match @dummy.avatar.public_url, @dummy.avatar.expiring_url
604
+ end
605
+ end
606
+ end