activestorage 5.2.6.3 → 6.0.0.beta1
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.
Potentially problematic release.
This version of activestorage might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +102 -134
- data/MIT-LICENSE +1 -1
- data/README.md +6 -5
- data/app/assets/javascripts/activestorage.js +4 -1
- data/app/controllers/active_storage/base_controller.rb +3 -5
- data/app/controllers/active_storage/blobs_controller.rb +1 -1
- data/app/controllers/active_storage/disk_controller.rb +4 -1
- data/app/controllers/active_storage/representations_controller.rb +1 -1
- data/app/controllers/concerns/active_storage/set_current.rb +15 -0
- data/app/javascript/activestorage/blob_record.js +6 -1
- data/app/jobs/active_storage/analyze_job.rb +4 -0
- data/app/jobs/active_storage/base_job.rb +0 -1
- data/app/jobs/active_storage/purge_job.rb +3 -0
- data/app/models/active_storage/attachment.rb +18 -9
- data/app/models/active_storage/blob/representable.rb +5 -5
- data/app/models/active_storage/blob.rb +63 -22
- data/app/models/active_storage/filename.rb +0 -6
- data/app/models/active_storage/preview.rb +3 -3
- data/app/models/active_storage/variant.rb +51 -52
- data/app/models/active_storage/variation.rb +23 -384
- data/config/routes.rb +13 -12
- data/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb +7 -0
- data/lib/active_storage/analyzer/video_analyzer.rb +2 -4
- data/lib/active_storage/analyzer.rb +9 -4
- data/lib/active_storage/attached/changes/create_many.rb +46 -0
- data/lib/active_storage/attached/changes/create_one.rb +68 -0
- data/lib/active_storage/attached/changes/create_one_of_many.rb +10 -0
- data/lib/active_storage/attached/changes/delete_many.rb +23 -0
- data/lib/active_storage/attached/changes/delete_one.rb +19 -0
- data/lib/active_storage/attached/changes.rb +16 -0
- data/lib/active_storage/attached/many.rb +16 -10
- data/lib/active_storage/attached/model.rb +140 -0
- data/lib/active_storage/attached/one.rb +16 -19
- data/lib/active_storage/attached.rb +7 -22
- data/lib/active_storage/downloader.rb +44 -0
- data/lib/active_storage/downloading.rb +8 -0
- data/lib/active_storage/engine.rb +36 -23
- data/lib/active_storage/errors.rb +22 -3
- data/lib/active_storage/gem_version.rb +4 -4
- data/lib/active_storage/previewer/poppler_pdf_previewer.rb +3 -3
- data/lib/active_storage/previewer/video_previewer.rb +2 -3
- data/lib/active_storage/previewer.rb +21 -11
- data/lib/active_storage/reflection.rb +64 -0
- data/lib/active_storage/service/azure_storage_service.rb +30 -14
- data/lib/active_storage/service/configurator.rb +3 -1
- data/lib/active_storage/service/disk_service.rb +20 -16
- data/lib/active_storage/service/gcs_service.rb +48 -46
- data/lib/active_storage/service/mirror_service.rb +1 -1
- data/lib/active_storage/service/s3_service.rb +10 -9
- data/lib/active_storage/service.rb +5 -6
- data/lib/active_storage/transformers/image_processing_transformer.rb +39 -0
- data/lib/active_storage/transformers/mini_magick_transformer.rb +38 -0
- data/lib/active_storage/transformers/transformer.rb +42 -0
- data/lib/active_storage.rb +13 -4
- data/lib/tasks/activestorage.rake +7 -0
- metadata +31 -19
- data/app/models/active_storage/filename/parameters.rb +0 -36
- data/lib/active_storage/attached/macros.rb +0 -110
@@ -6,315 +6,12 @@
|
|
6
6
|
# In case you do need to use this directly, it's instantiated using a hash of transformations where
|
7
7
|
# the key is the command and the value is the arguments. Example:
|
8
8
|
#
|
9
|
-
# ActiveStorage::Variation.new(
|
9
|
+
# ActiveStorage::Variation.new(resize_to_fit: [100, 100], monochrome: true, trim: true, rotate: "-90")
|
10
10
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# ActiveStorage::Variation.new(combine_options: {
|
14
|
-
# resize: "100x100^",
|
15
|
-
# gravity: "center",
|
16
|
-
# crop: "100x100+0+0",
|
17
|
-
# })
|
18
|
-
#
|
19
|
-
# A list of all possible transformations is available at https://www.imagemagick.org/script/mogrify.php.
|
11
|
+
# The options map directly to {ImageProcessing}[https://github.com/janko-m/image_processing] commands.
|
20
12
|
class ActiveStorage::Variation
|
21
13
|
attr_reader :transformations
|
22
14
|
|
23
|
-
class UnsupportedImageProcessingMethod < StandardError; end
|
24
|
-
class UnsupportedImageProcessingArgument < StandardError; end
|
25
|
-
|
26
|
-
SUPPORTED_IMAGE_PROCESSING_METHODS = [
|
27
|
-
"adaptive_blur",
|
28
|
-
"adaptive_resize",
|
29
|
-
"adaptive_sharpen",
|
30
|
-
"adjoin",
|
31
|
-
"affine",
|
32
|
-
"alpha",
|
33
|
-
"annotate",
|
34
|
-
"antialias",
|
35
|
-
"append",
|
36
|
-
"apply",
|
37
|
-
"attenuate",
|
38
|
-
"authenticate",
|
39
|
-
"auto_gamma",
|
40
|
-
"auto_level",
|
41
|
-
"auto_orient",
|
42
|
-
"auto_threshold",
|
43
|
-
"backdrop",
|
44
|
-
"background",
|
45
|
-
"bench",
|
46
|
-
"bias",
|
47
|
-
"bilateral_blur",
|
48
|
-
"black_point_compensation",
|
49
|
-
"black_threshold",
|
50
|
-
"blend",
|
51
|
-
"blue_primary",
|
52
|
-
"blue_shift",
|
53
|
-
"blur",
|
54
|
-
"border",
|
55
|
-
"bordercolor",
|
56
|
-
"borderwidth",
|
57
|
-
"brightness_contrast",
|
58
|
-
"cache",
|
59
|
-
"canny",
|
60
|
-
"caption",
|
61
|
-
"channel",
|
62
|
-
"channel_fx",
|
63
|
-
"charcoal",
|
64
|
-
"chop",
|
65
|
-
"clahe",
|
66
|
-
"clamp",
|
67
|
-
"clip",
|
68
|
-
"clip_path",
|
69
|
-
"clone",
|
70
|
-
"clut",
|
71
|
-
"coalesce",
|
72
|
-
"colorize",
|
73
|
-
"colormap",
|
74
|
-
"color_matrix",
|
75
|
-
"colors",
|
76
|
-
"colorspace",
|
77
|
-
"colourspace",
|
78
|
-
"color_threshold",
|
79
|
-
"combine",
|
80
|
-
"combine_options",
|
81
|
-
"comment",
|
82
|
-
"compare",
|
83
|
-
"complex",
|
84
|
-
"compose",
|
85
|
-
"composite",
|
86
|
-
"compress",
|
87
|
-
"connected_components",
|
88
|
-
"contrast",
|
89
|
-
"contrast_stretch",
|
90
|
-
"convert",
|
91
|
-
"convolve",
|
92
|
-
"copy",
|
93
|
-
"crop",
|
94
|
-
"cycle",
|
95
|
-
"deconstruct",
|
96
|
-
"define",
|
97
|
-
"delay",
|
98
|
-
"delete",
|
99
|
-
"density",
|
100
|
-
"depth",
|
101
|
-
"descend",
|
102
|
-
"deskew",
|
103
|
-
"despeckle",
|
104
|
-
"direction",
|
105
|
-
"displace",
|
106
|
-
"dispose",
|
107
|
-
"dissimilarity_threshold",
|
108
|
-
"dissolve",
|
109
|
-
"distort",
|
110
|
-
"dither",
|
111
|
-
"draw",
|
112
|
-
"duplicate",
|
113
|
-
"edge",
|
114
|
-
"emboss",
|
115
|
-
"encoding",
|
116
|
-
"endian",
|
117
|
-
"enhance",
|
118
|
-
"equalize",
|
119
|
-
"evaluate",
|
120
|
-
"evaluate_sequence",
|
121
|
-
"extent",
|
122
|
-
"extract",
|
123
|
-
"family",
|
124
|
-
"features",
|
125
|
-
"fft",
|
126
|
-
"fill",
|
127
|
-
"filter",
|
128
|
-
"flatten",
|
129
|
-
"flip",
|
130
|
-
"floodfill",
|
131
|
-
"flop",
|
132
|
-
"font",
|
133
|
-
"foreground",
|
134
|
-
"format",
|
135
|
-
"frame",
|
136
|
-
"function",
|
137
|
-
"fuzz",
|
138
|
-
"fx",
|
139
|
-
"gamma",
|
140
|
-
"gaussian_blur",
|
141
|
-
"geometry",
|
142
|
-
"gravity",
|
143
|
-
"grayscale",
|
144
|
-
"green_primary",
|
145
|
-
"hald_clut",
|
146
|
-
"highlight_color",
|
147
|
-
"hough_lines",
|
148
|
-
"iconGeometry",
|
149
|
-
"iconic",
|
150
|
-
"identify",
|
151
|
-
"ift",
|
152
|
-
"illuminant",
|
153
|
-
"immutable",
|
154
|
-
"implode",
|
155
|
-
"insert",
|
156
|
-
"intensity",
|
157
|
-
"intent",
|
158
|
-
"interlace",
|
159
|
-
"interline_spacing",
|
160
|
-
"interpolate",
|
161
|
-
"interpolative_resize",
|
162
|
-
"interword_spacing",
|
163
|
-
"kerning",
|
164
|
-
"kmeans",
|
165
|
-
"kuwahara",
|
166
|
-
"label",
|
167
|
-
"lat",
|
168
|
-
"layers",
|
169
|
-
"level",
|
170
|
-
"level_colors",
|
171
|
-
"limit",
|
172
|
-
"limits",
|
173
|
-
"linear_stretch",
|
174
|
-
"linewidth",
|
175
|
-
"liquid_rescale",
|
176
|
-
"list",
|
177
|
-
"loader",
|
178
|
-
"log",
|
179
|
-
"loop",
|
180
|
-
"lowlight_color",
|
181
|
-
"magnify",
|
182
|
-
"map",
|
183
|
-
"mattecolor",
|
184
|
-
"median",
|
185
|
-
"mean_shift",
|
186
|
-
"metric",
|
187
|
-
"mode",
|
188
|
-
"modulate",
|
189
|
-
"moments",
|
190
|
-
"monitor",
|
191
|
-
"monochrome",
|
192
|
-
"morph",
|
193
|
-
"morphology",
|
194
|
-
"mosaic",
|
195
|
-
"motion_blur",
|
196
|
-
"name",
|
197
|
-
"negate",
|
198
|
-
"noise",
|
199
|
-
"normalize",
|
200
|
-
"opaque",
|
201
|
-
"ordered_dither",
|
202
|
-
"orient",
|
203
|
-
"page",
|
204
|
-
"paint",
|
205
|
-
"pause",
|
206
|
-
"perceptible",
|
207
|
-
"ping",
|
208
|
-
"pointsize",
|
209
|
-
"polaroid",
|
210
|
-
"poly",
|
211
|
-
"posterize",
|
212
|
-
"precision",
|
213
|
-
"preview",
|
214
|
-
"process",
|
215
|
-
"quality",
|
216
|
-
"quantize",
|
217
|
-
"quiet",
|
218
|
-
"radial_blur",
|
219
|
-
"raise",
|
220
|
-
"random_threshold",
|
221
|
-
"range_threshold",
|
222
|
-
"red_primary",
|
223
|
-
"regard_warnings",
|
224
|
-
"region",
|
225
|
-
"remote",
|
226
|
-
"render",
|
227
|
-
"repage",
|
228
|
-
"resample",
|
229
|
-
"resize",
|
230
|
-
"resize_to_fill",
|
231
|
-
"resize_to_fit",
|
232
|
-
"resize_to_limit",
|
233
|
-
"resize_and_pad",
|
234
|
-
"respect_parentheses",
|
235
|
-
"reverse",
|
236
|
-
"roll",
|
237
|
-
"rotate",
|
238
|
-
"sample",
|
239
|
-
"sampling_factor",
|
240
|
-
"saver",
|
241
|
-
"scale",
|
242
|
-
"scene",
|
243
|
-
"screen",
|
244
|
-
"seed",
|
245
|
-
"segment",
|
246
|
-
"selective_blur",
|
247
|
-
"separate",
|
248
|
-
"sepia_tone",
|
249
|
-
"shade",
|
250
|
-
"shadow",
|
251
|
-
"shared_memory",
|
252
|
-
"sharpen",
|
253
|
-
"shave",
|
254
|
-
"shear",
|
255
|
-
"sigmoidal_contrast",
|
256
|
-
"silent",
|
257
|
-
"similarity_threshold",
|
258
|
-
"size",
|
259
|
-
"sketch",
|
260
|
-
"smush",
|
261
|
-
"snaps",
|
262
|
-
"solarize",
|
263
|
-
"sort_pixels",
|
264
|
-
"sparse_color",
|
265
|
-
"splice",
|
266
|
-
"spread",
|
267
|
-
"statistic",
|
268
|
-
"stegano",
|
269
|
-
"stereo",
|
270
|
-
"storage_type",
|
271
|
-
"stretch",
|
272
|
-
"strip",
|
273
|
-
"stroke",
|
274
|
-
"strokewidth",
|
275
|
-
"style",
|
276
|
-
"subimage_search",
|
277
|
-
"swap",
|
278
|
-
"swirl",
|
279
|
-
"synchronize",
|
280
|
-
"taint",
|
281
|
-
"text_font",
|
282
|
-
"threshold",
|
283
|
-
"thumbnail",
|
284
|
-
"tile_offset",
|
285
|
-
"tint",
|
286
|
-
"title",
|
287
|
-
"transform",
|
288
|
-
"transparent",
|
289
|
-
"transparent_color",
|
290
|
-
"transpose",
|
291
|
-
"transverse",
|
292
|
-
"treedepth",
|
293
|
-
"trim",
|
294
|
-
"type",
|
295
|
-
"undercolor",
|
296
|
-
"unique_colors",
|
297
|
-
"units",
|
298
|
-
"unsharp",
|
299
|
-
"update",
|
300
|
-
"valid_image",
|
301
|
-
"view",
|
302
|
-
"vignette",
|
303
|
-
"virtual_pixel",
|
304
|
-
"visual",
|
305
|
-
"watermark",
|
306
|
-
"wave",
|
307
|
-
"wavelet_denoise",
|
308
|
-
"weight",
|
309
|
-
"white_balance",
|
310
|
-
"white_point",
|
311
|
-
"white_threshold",
|
312
|
-
"window",
|
313
|
-
"window_group",
|
314
|
-
].concat(ActiveStorage.supported_image_processing_methods)
|
315
|
-
|
316
|
-
UNSUPPORTED_IMAGE_PROCESSING_ARGUMENTS = ActiveStorage.unsupported_image_processing_arguments
|
317
|
-
|
318
15
|
class << self
|
319
16
|
# Returns a Variation instance based on the given variator. If the variator is a Variation, it is
|
320
17
|
# returned unmodified. If it is a String, it is passed to ActiveStorage::Variation.decode. Otherwise,
|
@@ -346,24 +43,13 @@ class ActiveStorage::Variation
|
|
346
43
|
@transformations = transformations
|
347
44
|
end
|
348
45
|
|
349
|
-
# Accepts
|
350
|
-
#
|
351
|
-
|
46
|
+
# Accepts a File object, performs the +transformations+ against it, and
|
47
|
+
# saves the transformed image into a temporary file. If +format+ is specified
|
48
|
+
# it will be the format of the result image, otherwise the result image
|
49
|
+
# retains the source format.
|
50
|
+
def transform(file, format: nil, &block)
|
352
51
|
ActiveSupport::Notifications.instrument("transform.active_storage") do
|
353
|
-
|
354
|
-
validate_transformation(name, argument_or_subtransformations)
|
355
|
-
image.mogrify do |command|
|
356
|
-
if name.to_s == "combine_options"
|
357
|
-
argument_or_subtransformations.each do |subtransformation_name, subtransformation_argument|
|
358
|
-
validate_transformation(subtransformation_name, subtransformation_argument)
|
359
|
-
pass_transform_argument(command, subtransformation_name, subtransformation_argument)
|
360
|
-
end
|
361
|
-
else
|
362
|
-
validate_transformation(name, argument_or_subtransformations)
|
363
|
-
pass_transform_argument(command, name, argument_or_subtransformations)
|
364
|
-
end
|
365
|
-
end
|
366
|
-
end
|
52
|
+
transformer.transform(file, format: format, &block)
|
367
53
|
end
|
368
54
|
end
|
369
55
|
|
@@ -373,69 +59,22 @@ class ActiveStorage::Variation
|
|
373
59
|
end
|
374
60
|
|
375
61
|
private
|
376
|
-
def
|
377
|
-
if
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
method_name = name.to_s.gsub("-","_")
|
390
|
-
|
391
|
-
unless SUPPORTED_IMAGE_PROCESSING_METHODS.any? { |method| method_name == method }
|
392
|
-
raise UnsupportedImageProcessingMethod, <<~ERROR.squish
|
393
|
-
One or more of the provided transformation methods is not supported.
|
394
|
-
ERROR
|
395
|
-
end
|
396
|
-
|
397
|
-
if argument.present?
|
398
|
-
if argument.is_a?(String) || argument.is_a?(Symbol)
|
399
|
-
validate_arg_string(argument)
|
400
|
-
elsif argument.is_a?(Array)
|
401
|
-
validate_arg_array(argument)
|
402
|
-
elsif argument.is_a?(Hash)
|
403
|
-
validate_arg_hash(argument)
|
404
|
-
end
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
def validate_arg_string(argument)
|
409
|
-
if UNSUPPORTED_IMAGE_PROCESSING_ARGUMENTS.any? { |bad_arg| argument.to_s.downcase.include?(bad_arg) }; raise UnsupportedImageProcessingArgument end
|
410
|
-
end
|
411
|
-
|
412
|
-
def validate_arg_array(argument)
|
413
|
-
argument.each do |arg|
|
414
|
-
if arg.is_a?(Integer) || arg.is_a?(Float)
|
415
|
-
next
|
416
|
-
elsif arg.is_a?(String) || arg.is_a?(Symbol)
|
417
|
-
validate_arg_string(arg)
|
418
|
-
elsif arg.is_a?(Array)
|
419
|
-
validate_arg_array(arg)
|
420
|
-
elsif arg.is_a?(Hash)
|
421
|
-
validate_arg_hash(arg)
|
422
|
-
end
|
423
|
-
end
|
424
|
-
end
|
425
|
-
|
426
|
-
def validate_arg_hash(argument)
|
427
|
-
argument.each do |key, value|
|
428
|
-
validate_arg_string(key)
|
429
|
-
|
430
|
-
if value.is_a?(Integer) || value.is_a?(Float)
|
431
|
-
next
|
432
|
-
elsif value.is_a?(String) || value.is_a?(Symbol)
|
433
|
-
validate_arg_string(value)
|
434
|
-
elsif value.is_a?(Array)
|
435
|
-
validate_arg_array(value)
|
436
|
-
elsif value.is_a?(Hash)
|
437
|
-
validate_arg_hash(value)
|
62
|
+
def transformer
|
63
|
+
if ActiveStorage.variant_processor
|
64
|
+
begin
|
65
|
+
require "image_processing"
|
66
|
+
rescue LoadError
|
67
|
+
ActiveSupport::Deprecation.warn <<~WARNING
|
68
|
+
Generating image variants will require the image_processing gem in Rails 6.1.
|
69
|
+
Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.
|
70
|
+
WARNING
|
71
|
+
|
72
|
+
ActiveStorage::Transformers::MiniMagickTransformer.new(transformations)
|
73
|
+
else
|
74
|
+
ActiveStorage::Transformers::ImageProcessingTransformer.new(transformations)
|
438
75
|
end
|
76
|
+
else
|
77
|
+
ActiveStorage::Transformers::MiniMagickTransformer.new(transformations)
|
439
78
|
end
|
440
79
|
end
|
441
80
|
end
|
data/config/routes.rb
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Rails.application.routes.draw do
|
4
|
-
|
5
|
-
|
6
|
-
direct :rails_blob do |blob, options|
|
7
|
-
route_for(:rails_service_blob, blob.signed_id, blob.filename, options)
|
8
|
-
end
|
9
|
-
|
10
|
-
resolve("ActiveStorage::Blob") { |blob, options| route_for(:rails_blob, blob, options) }
|
11
|
-
resolve("ActiveStorage::Attachment") { |attachment, options| route_for(:rails_blob, attachment.blob, options) }
|
4
|
+
scope ActiveStorage.routes_prefix do
|
5
|
+
get "/blobs/:signed_id/*filename" => "active_storage/blobs#show", as: :rails_service_blob
|
12
6
|
|
7
|
+
get "/representations/:signed_blob_id/:variation_key/*filename" => "active_storage/representations#show", as: :rails_blob_representation
|
13
8
|
|
14
|
-
|
9
|
+
get "/disk/:encoded_key/*filename" => "active_storage/disk#show", as: :rails_disk_service
|
10
|
+
put "/disk/:encoded_token" => "active_storage/disk#update", as: :update_rails_disk_service
|
11
|
+
post "/direct_uploads" => "active_storage/direct_uploads#create", as: :rails_direct_uploads
|
12
|
+
end
|
15
13
|
|
16
14
|
direct :rails_representation do |representation, options|
|
17
15
|
signed_blob_id = representation.blob.signed_id
|
@@ -25,7 +23,10 @@ Rails.application.routes.draw do
|
|
25
23
|
resolve("ActiveStorage::Preview") { |preview, options| route_for(:rails_representation, preview, options) }
|
26
24
|
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
direct :rails_blob do |blob, options|
|
27
|
+
route_for(:rails_service_blob, blob.signed_id, blob.filename, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
resolve("ActiveStorage::Blob") { |blob, options| route_for(:rails_blob, blob, options) }
|
31
|
+
resolve("ActiveStorage::Attachment") { |attachment, options| route_for(:rails_blob, attachment.blob, options) }
|
31
32
|
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class AddForeignKeyConstraintToActiveStorageAttachmentsForBlobId < ActiveRecord::Migration[6.0]
|
2
|
+
def up
|
3
|
+
unless foreign_key_exists?(:active_storage_attachments, column: :blob_id)
|
4
|
+
add_foreign_key :active_storage_attachments, :active_storage_blobs, column: :blob_id
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/hash/compact"
|
4
|
-
|
5
3
|
module ActiveStorage
|
6
4
|
# Extracts the following from a video blob:
|
7
5
|
#
|
@@ -18,7 +16,7 @@ module ActiveStorage
|
|
18
16
|
#
|
19
17
|
# When a video's angle is 90 or 270 degrees, its width and height are automatically swapped for convenience.
|
20
18
|
#
|
21
|
-
# This analyzer requires the {
|
19
|
+
# This analyzer requires the {FFmpeg}[https://www.ffmpeg.org] system library, which is not provided by Rails.
|
22
20
|
class Analyzer::VideoAnalyzer < Analyzer
|
23
21
|
def self.accept?(blob)
|
24
22
|
blob.video?
|
@@ -109,7 +107,7 @@ module ActiveStorage
|
|
109
107
|
JSON.parse(output.read)
|
110
108
|
end
|
111
109
|
rescue Errno::ENOENT
|
112
|
-
logger.info "Skipping video analysis because
|
110
|
+
logger.info "Skipping video analysis because FFmpeg isn't installed"
|
113
111
|
{}
|
114
112
|
end
|
115
113
|
|
@@ -1,13 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_storage/downloading"
|
4
|
-
|
5
3
|
module ActiveStorage
|
6
4
|
# This is an abstract base class for analyzers, which extract metadata from blobs. See
|
7
5
|
# ActiveStorage::Analyzer::ImageAnalyzer for an example of a concrete subclass.
|
8
6
|
class Analyzer
|
9
|
-
include Downloading
|
10
|
-
|
11
7
|
attr_reader :blob
|
12
8
|
|
13
9
|
# Implement this method in a concrete subclass. Have it return true when given a blob from which
|
@@ -26,8 +22,17 @@ module ActiveStorage
|
|
26
22
|
end
|
27
23
|
|
28
24
|
private
|
25
|
+
# Downloads the blob to a tempfile on disk. Yields the tempfile.
|
26
|
+
def download_blob_to_tempfile(&block) #:doc:
|
27
|
+
blob.open tempdir: tempdir, &block
|
28
|
+
end
|
29
|
+
|
29
30
|
def logger #:doc:
|
30
31
|
ActiveStorage.logger
|
31
32
|
end
|
33
|
+
|
34
|
+
def tempdir #:doc:
|
35
|
+
Dir.tmpdir
|
36
|
+
end
|
32
37
|
end
|
33
38
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveStorage
|
4
|
+
class Attached::Changes::CreateMany #:nodoc:
|
5
|
+
attr_reader :name, :record, :attachables
|
6
|
+
|
7
|
+
def initialize(name, record, attachables)
|
8
|
+
@name, @record, @attachables = name, record, Array(attachables)
|
9
|
+
end
|
10
|
+
|
11
|
+
def attachments
|
12
|
+
@attachments ||= subchanges.collect(&:attachment)
|
13
|
+
end
|
14
|
+
|
15
|
+
def blobs
|
16
|
+
@blobs ||= subchanges.collect(&:blob)
|
17
|
+
end
|
18
|
+
|
19
|
+
def upload
|
20
|
+
subchanges.each(&:upload)
|
21
|
+
end
|
22
|
+
|
23
|
+
def save
|
24
|
+
assign_associated_attachments
|
25
|
+
reset_associated_blobs
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def subchanges
|
30
|
+
@subchanges ||= attachables.collect { |attachable| build_subchange_from(attachable) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_subchange_from(attachable)
|
34
|
+
ActiveStorage::Attached::Changes::CreateOneOfMany.new(name, record, attachable)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def assign_associated_attachments
|
39
|
+
record.public_send("#{name}_attachments=", attachments)
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset_associated_blobs
|
43
|
+
record.public_send("#{name}_blobs").reset
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_dispatch"
|
4
|
+
require "action_dispatch/http/upload"
|
5
|
+
|
6
|
+
module ActiveStorage
|
7
|
+
class Attached::Changes::CreateOne #:nodoc:
|
8
|
+
attr_reader :name, :record, :attachable
|
9
|
+
|
10
|
+
def initialize(name, record, attachable)
|
11
|
+
@name, @record, @attachable = name, record, attachable
|
12
|
+
end
|
13
|
+
|
14
|
+
def attachment
|
15
|
+
@attachment ||= find_or_build_attachment
|
16
|
+
end
|
17
|
+
|
18
|
+
def blob
|
19
|
+
@blob ||= find_or_build_blob
|
20
|
+
end
|
21
|
+
|
22
|
+
def upload
|
23
|
+
case attachable
|
24
|
+
when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
|
25
|
+
blob.upload_without_unfurling(attachable.open)
|
26
|
+
when Hash
|
27
|
+
blob.upload_without_unfurling(attachable.fetch(:io))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def save
|
32
|
+
record.public_send("#{name}_attachment=", attachment)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def find_or_build_attachment
|
37
|
+
find_attachment || build_attachment
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_attachment
|
41
|
+
if record.public_send("#{name}_blob") == blob
|
42
|
+
record.public_send("#{name}_attachment")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_attachment
|
47
|
+
ActiveStorage::Attachment.new(record: record, name: name, blob: blob)
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_or_build_blob
|
51
|
+
case attachable
|
52
|
+
when ActiveStorage::Blob
|
53
|
+
attachable
|
54
|
+
when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
|
55
|
+
ActiveStorage::Blob.build_after_unfurling \
|
56
|
+
io: attachable.open,
|
57
|
+
filename: attachable.original_filename,
|
58
|
+
content_type: attachable.content_type
|
59
|
+
when Hash
|
60
|
+
ActiveStorage::Blob.build_after_unfurling(attachable)
|
61
|
+
when String
|
62
|
+
ActiveStorage::Blob.find_signed(attachable)
|
63
|
+
else
|
64
|
+
raise ArgumentError, "Could not find or build blob: expected attachable, got #{attachable.inspect}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveStorage
|
4
|
+
class Attached::Changes::CreateOneOfMany < Attached::Changes::CreateOne #:nodoc:
|
5
|
+
private
|
6
|
+
def find_attachment
|
7
|
+
record.public_send("#{name}_attachments").detect { |attachment| attachment.blob_id == blob.id }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveStorage
|
4
|
+
class Attached::Changes::DeleteMany #:nodoc:
|
5
|
+
attr_reader :name, :record
|
6
|
+
|
7
|
+
def initialize(name, record)
|
8
|
+
@name, @record = name, record
|
9
|
+
end
|
10
|
+
|
11
|
+
def attachments
|
12
|
+
ActiveStorage::Attachment.none
|
13
|
+
end
|
14
|
+
|
15
|
+
def blobs
|
16
|
+
ActiveStorage::Blob.none
|
17
|
+
end
|
18
|
+
|
19
|
+
def save
|
20
|
+
record.public_send("#{name}_attachments=", [])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|