activestorage 5.2.7.1 → 6.1.4.6
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activestorage might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +225 -93
- data/MIT-LICENSE +1 -1
- data/README.md +43 -8
- data/app/assets/javascripts/activestorage.js +5 -2
- data/app/controllers/active_storage/base_controller.rb +13 -4
- data/app/controllers/active_storage/blobs/proxy_controller.rb +14 -0
- data/app/controllers/active_storage/{blobs_controller.rb → blobs/redirect_controller.rb} +3 -3
- data/app/controllers/active_storage/direct_uploads_controller.rb +2 -2
- data/app/controllers/active_storage/disk_controller.rb +13 -22
- data/app/controllers/active_storage/representations/base_controller.rb +14 -0
- data/app/controllers/active_storage/representations/proxy_controller.rb +13 -0
- data/app/controllers/active_storage/{representations_controller.rb → representations/redirect_controller.rb} +3 -5
- data/app/controllers/concerns/active_storage/file_server.rb +18 -0
- data/app/controllers/concerns/active_storage/set_blob.rb +1 -1
- data/app/controllers/concerns/active_storage/set_current.rb +15 -0
- data/app/controllers/concerns/active_storage/set_headers.rb +12 -0
- data/app/javascript/activestorage/blob_record.js +7 -2
- data/app/jobs/active_storage/analyze_job.rb +5 -0
- data/app/jobs/active_storage/base_job.rb +0 -1
- data/app/jobs/active_storage/mirror_job.rb +15 -0
- data/app/jobs/active_storage/purge_job.rb +3 -0
- data/app/models/active_storage/attachment.rb +35 -16
- data/app/models/active_storage/blob/analyzable.rb +6 -2
- data/app/models/active_storage/blob/identifiable.rb +7 -6
- data/app/models/active_storage/blob/representable.rb +36 -6
- data/app/models/active_storage/blob.rb +186 -68
- data/app/models/active_storage/filename.rb +0 -6
- data/app/models/active_storage/preview.rb +37 -12
- data/app/models/active_storage/record.rb +7 -0
- data/app/models/active_storage/variant.rb +53 -67
- data/app/models/active_storage/variant_record.rb +8 -0
- data/app/models/active_storage/variant_with_record.rb +54 -0
- data/app/models/active_storage/variation.rb +30 -94
- data/config/routes.rb +66 -15
- data/db/migrate/20170806125915_create_active_storage_tables.rb +14 -5
- data/db/update_migrate/20190112182829_add_service_name_to_active_storage_blobs.rb +17 -0
- data/db/update_migrate/20191206030411_create_active_storage_variant_records.rb +11 -0
- data/lib/active_storage/analyzer/image_analyzer.rb +14 -4
- data/lib/active_storage/analyzer/null_analyzer.rb +4 -0
- data/lib/active_storage/analyzer/video_analyzer.rb +17 -8
- data/lib/active_storage/analyzer.rb +15 -4
- data/lib/active_storage/attached/changes/create_many.rb +47 -0
- data/lib/active_storage/attached/changes/create_one.rb +82 -0
- data/lib/active_storage/attached/changes/create_one_of_many.rb +10 -0
- data/lib/active_storage/attached/changes/delete_many.rb +27 -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 +19 -12
- data/lib/active_storage/attached/model.rb +212 -0
- data/lib/active_storage/attached/one.rb +19 -21
- data/lib/active_storage/attached.rb +7 -22
- data/lib/active_storage/downloader.rb +43 -0
- data/lib/active_storage/engine.rb +60 -38
- data/lib/active_storage/errors.rb +25 -3
- data/lib/active_storage/gem_version.rb +4 -4
- data/lib/active_storage/log_subscriber.rb +6 -0
- data/lib/active_storage/previewer/mupdf_previewer.rb +3 -3
- data/lib/active_storage/previewer/poppler_pdf_previewer.rb +3 -3
- data/lib/active_storage/previewer/video_previewer.rb +17 -10
- data/lib/active_storage/previewer.rb +34 -14
- data/lib/active_storage/reflection.rb +64 -0
- data/lib/active_storage/service/azure_storage_service.rb +65 -44
- data/lib/active_storage/service/configurator.rb +6 -2
- data/lib/active_storage/service/disk_service.rb +57 -44
- data/lib/active_storage/service/gcs_service.rb +68 -64
- data/lib/active_storage/service/mirror_service.rb +31 -7
- data/lib/active_storage/service/registry.rb +32 -0
- data/lib/active_storage/service/s3_service.rb +56 -24
- data/lib/active_storage/service.rb +44 -12
- data/lib/active_storage/transformers/image_processing_transformer.rb +45 -0
- data/lib/active_storage/transformers/transformer.rb +39 -0
- data/lib/active_storage.rb +31 -296
- data/lib/tasks/activestorage.rake +11 -0
- metadata +82 -16
- data/app/models/active_storage/filename/parameters.rb +0 -36
- data/lib/active_storage/attached/macros.rb +0 -110
- data/lib/active_storage/downloading.rb +0 -39
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveStorage
|
4
|
+
class Downloader #:nodoc:
|
5
|
+
attr_reader :service
|
6
|
+
|
7
|
+
def initialize(service)
|
8
|
+
@service = service
|
9
|
+
end
|
10
|
+
|
11
|
+
def open(key, checksum:, name: "ActiveStorage-", tmpdir: nil)
|
12
|
+
open_tempfile(name, tmpdir) do |file|
|
13
|
+
download key, file
|
14
|
+
verify_integrity_of file, checksum: checksum
|
15
|
+
yield file
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def open_tempfile(name, tmpdir = nil)
|
21
|
+
file = Tempfile.open(name, tmpdir)
|
22
|
+
|
23
|
+
begin
|
24
|
+
yield file
|
25
|
+
ensure
|
26
|
+
file.close!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def download(key, file)
|
31
|
+
file.binmode
|
32
|
+
service.download(key) { |chunk| file.write(chunk) }
|
33
|
+
file.flush
|
34
|
+
file.rewind
|
35
|
+
end
|
36
|
+
|
37
|
+
def verify_integrity_of(file, checksum:)
|
38
|
+
unless Digest::MD5.file(file).base64digest == checksum
|
39
|
+
raise ActiveStorage::IntegrityError
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "rails"
|
4
|
+
require "action_controller/railtie"
|
5
|
+
require "active_job/railtie"
|
6
|
+
require "active_record/railtie"
|
7
|
+
|
4
8
|
require "active_storage"
|
5
9
|
|
6
10
|
require "active_storage/previewer/poppler_pdf_previewer"
|
@@ -10,6 +14,10 @@ require "active_storage/previewer/video_previewer"
|
|
10
14
|
require "active_storage/analyzer/image_analyzer"
|
11
15
|
require "active_storage/analyzer/video_analyzer"
|
12
16
|
|
17
|
+
require "active_storage/service/registry"
|
18
|
+
|
19
|
+
require "active_storage/reflection"
|
20
|
+
|
13
21
|
module ActiveStorage
|
14
22
|
class Engine < Rails::Engine # :nodoc:
|
15
23
|
isolate_namespace ActiveStorage
|
@@ -18,14 +26,26 @@ module ActiveStorage
|
|
18
26
|
config.active_storage.previewers = [ ActiveStorage::Previewer::PopplerPDFPreviewer, ActiveStorage::Previewer::MuPDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ]
|
19
27
|
config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer ]
|
20
28
|
config.active_storage.paths = ActiveSupport::OrderedOptions.new
|
29
|
+
config.active_storage.queues = ActiveSupport::InheritableOptions.new
|
21
30
|
|
22
31
|
config.active_storage.variable_content_types = %w(
|
23
32
|
image/png
|
24
33
|
image/gif
|
25
34
|
image/jpg
|
26
35
|
image/jpeg
|
36
|
+
image/pjpeg
|
37
|
+
image/tiff
|
38
|
+
image/bmp
|
27
39
|
image/vnd.adobe.photoshop
|
28
40
|
image/vnd.microsoft.icon
|
41
|
+
image/webp
|
42
|
+
)
|
43
|
+
|
44
|
+
config.active_storage.web_image_content_types = %w(
|
45
|
+
image/png
|
46
|
+
image/jpeg
|
47
|
+
image/jpg
|
48
|
+
image/gif
|
29
49
|
)
|
30
50
|
|
31
51
|
config.active_storage.content_types_to_serve_as_binary = %w(
|
@@ -46,6 +66,8 @@ module ActiveStorage
|
|
46
66
|
image/gif
|
47
67
|
image/jpg
|
48
68
|
image/jpeg
|
69
|
+
image/tiff
|
70
|
+
image/bmp
|
49
71
|
image/vnd.adobe.photoshop
|
50
72
|
image/vnd.microsoft.icon
|
51
73
|
application/pdf
|
@@ -55,30 +77,25 @@ module ActiveStorage
|
|
55
77
|
|
56
78
|
initializer "active_storage.configs" do
|
57
79
|
config.after_initialize do |app|
|
58
|
-
ActiveStorage.logger
|
59
|
-
ActiveStorage.
|
60
|
-
ActiveStorage.previewers
|
61
|
-
ActiveStorage.analyzers
|
62
|
-
ActiveStorage.paths
|
63
|
-
|
64
|
-
ActiveStorage.
|
65
|
-
ActiveStorage.
|
66
|
-
|
67
|
-
-display
|
68
|
-
-distribute-cache
|
69
|
-
-help
|
70
|
-
-path
|
71
|
-
-print
|
72
|
-
-set
|
73
|
-
-verbose
|
74
|
-
-version
|
75
|
-
-write
|
76
|
-
-write-mask
|
77
|
-
)
|
80
|
+
ActiveStorage.logger = app.config.active_storage.logger || Rails.logger
|
81
|
+
ActiveStorage.variant_processor = app.config.active_storage.variant_processor || :mini_magick
|
82
|
+
ActiveStorage.previewers = app.config.active_storage.previewers || []
|
83
|
+
ActiveStorage.analyzers = app.config.active_storage.analyzers || []
|
84
|
+
ActiveStorage.paths = app.config.active_storage.paths || {}
|
85
|
+
ActiveStorage.routes_prefix = app.config.active_storage.routes_prefix || "/rails/active_storage"
|
86
|
+
ActiveStorage.draw_routes = app.config.active_storage.draw_routes != false
|
87
|
+
ActiveStorage.resolve_model_to_route = app.config.active_storage.resolve_model_to_route || :rails_storage_redirect
|
88
|
+
|
78
89
|
ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || []
|
90
|
+
ActiveStorage.web_image_content_types = app.config.active_storage.web_image_content_types || []
|
79
91
|
ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || []
|
92
|
+
ActiveStorage.service_urls_expire_in = app.config.active_storage.service_urls_expire_in || 5.minutes
|
80
93
|
ActiveStorage.content_types_allowed_inline = app.config.active_storage.content_types_allowed_inline || []
|
81
94
|
ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream"
|
95
|
+
ActiveStorage.video_preview_arguments = app.config.active_storage.video_preview_arguments || "-y -vframes 1 -f image2"
|
96
|
+
|
97
|
+
ActiveStorage.replace_on_assign_to_many = app.config.active_storage.replace_on_assign_to_many || false
|
98
|
+
ActiveStorage.track_variants = app.config.active_storage.track_variants || false
|
82
99
|
end
|
83
100
|
end
|
84
101
|
|
@@ -86,7 +103,7 @@ module ActiveStorage
|
|
86
103
|
require "active_storage/attached"
|
87
104
|
|
88
105
|
ActiveSupport.on_load(:active_record) do
|
89
|
-
|
106
|
+
include ActiveStorage::Attached::Model
|
90
107
|
end
|
91
108
|
end
|
92
109
|
|
@@ -98,29 +115,34 @@ module ActiveStorage
|
|
98
115
|
|
99
116
|
initializer "active_storage.services" do
|
100
117
|
ActiveSupport.on_load(:active_storage_blob) do
|
101
|
-
|
102
|
-
|
103
|
-
config_file =
|
118
|
+
configs = Rails.configuration.active_storage.service_configurations ||=
|
119
|
+
begin
|
120
|
+
config_file = Rails.root.join("config/storage/#{Rails.env}.yml")
|
121
|
+
config_file = Rails.root.join("config/storage.yml") unless config_file.exist?
|
104
122
|
raise("Couldn't find Active Storage configuration in #{config_file}") unless config_file.exist?
|
105
123
|
|
106
|
-
|
107
|
-
require "erb"
|
108
|
-
|
109
|
-
YAML.load(ERB.new(config_file.read).result) || {}
|
110
|
-
rescue Psych::SyntaxError => e
|
111
|
-
raise "YAML syntax error occurred while parsing #{config_file}. " \
|
112
|
-
"Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
|
113
|
-
"Error: #{e.message}"
|
124
|
+
ActiveSupport::ConfigurationFile.parse(config_file)
|
114
125
|
end
|
115
126
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
raise e, "Cannot load `Rails.config.active_storage.service`:\n#{e.message}", e.backtrace
|
121
|
-
end
|
127
|
+
ActiveStorage::Blob.services = ActiveStorage::Service::Registry.new(configs)
|
128
|
+
|
129
|
+
if config_choice = Rails.configuration.active_storage.service
|
130
|
+
ActiveStorage::Blob.service = ActiveStorage::Blob.services.fetch(config_choice)
|
122
131
|
end
|
123
132
|
end
|
124
133
|
end
|
134
|
+
|
135
|
+
initializer "active_storage.queues" do
|
136
|
+
config.after_initialize do |app|
|
137
|
+
ActiveStorage.queues = app.config.active_storage.queues || {}
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
initializer "active_storage.reflection" do
|
142
|
+
ActiveSupport.on_load(:active_record) do
|
143
|
+
include Reflection::ActiveRecordExtensions
|
144
|
+
ActiveRecord::Reflection.singleton_class.prepend(Reflection::ReflectionExtension)
|
145
|
+
end
|
146
|
+
end
|
125
147
|
end
|
126
148
|
end
|
@@ -1,7 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
|
-
class
|
5
|
-
class
|
6
|
-
|
4
|
+
# Generic base class for all Active Storage exceptions.
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
# Raised when ActiveStorage::Blob#variant is called on a blob that isn't variable.
|
8
|
+
# Use ActiveStorage::Blob#variable? to determine whether a blob is variable.
|
9
|
+
class InvariableError < Error; end
|
10
|
+
|
11
|
+
# Raised when ActiveStorage::Blob#preview is called on a blob that isn't previewable.
|
12
|
+
# Use ActiveStorage::Blob#previewable? to determine whether a blob is previewable.
|
13
|
+
class UnpreviewableError < Error; end
|
14
|
+
|
15
|
+
# Raised when ActiveStorage::Blob#representation is called on a blob that isn't representable.
|
16
|
+
# Use ActiveStorage::Blob#representable? to determine whether a blob is representable.
|
17
|
+
class UnrepresentableError < Error; end
|
18
|
+
|
19
|
+
# Raised when uploaded or downloaded data does not match a precomputed checksum.
|
20
|
+
# Indicates that a network error or a software bug caused data corruption.
|
21
|
+
class IntegrityError < Error; end
|
22
|
+
|
23
|
+
# Raised when ActiveStorage::Blob#download is called on a blob where the
|
24
|
+
# backing file is no longer present in its service.
|
25
|
+
class FileNotFoundError < Error; end
|
26
|
+
|
27
|
+
# Raised when a Previewer is unable to generate a preview image.
|
28
|
+
class PreviewError < Error; end
|
7
29
|
end
|
@@ -32,6 +32,12 @@ module ActiveStorage
|
|
32
32
|
debug event, color("Generated URL for file at key: #{key_in(event)} (#{event.payload[:url]})", BLUE)
|
33
33
|
end
|
34
34
|
|
35
|
+
def service_mirror(event)
|
36
|
+
message = "Mirrored file at key: #{key_in(event)}"
|
37
|
+
message += " (checksum: #{event.payload[:checksum]})" if event.payload[:checksum]
|
38
|
+
debug event, color(message, GREEN)
|
39
|
+
end
|
40
|
+
|
35
41
|
def logger
|
36
42
|
ActiveStorage.logger
|
37
43
|
end
|
@@ -12,7 +12,7 @@ module ActiveStorage
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def mutool_exists?
|
15
|
-
return @mutool_exists
|
15
|
+
return @mutool_exists if defined?(@mutool_exists) && !@mutool_exists.nil?
|
16
16
|
|
17
17
|
system mutool_path, out: File::NULL, err: File::NULL
|
18
18
|
|
@@ -20,10 +20,10 @@ module ActiveStorage
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def preview
|
23
|
+
def preview(**options)
|
24
24
|
download_blob_to_tempfile do |input|
|
25
25
|
draw_first_page_from input do |output|
|
26
|
-
yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png"
|
26
|
+
yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png", **options
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -12,16 +12,16 @@ module ActiveStorage
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def pdftoppm_exists?
|
15
|
-
return @pdftoppm_exists
|
15
|
+
return @pdftoppm_exists if defined?(@pdftoppm_exists)
|
16
16
|
|
17
17
|
@pdftoppm_exists = system(pdftoppm_path, "-v", out: File::NULL, err: File::NULL)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def preview
|
21
|
+
def preview(**options)
|
22
22
|
download_blob_to_tempfile do |input|
|
23
23
|
draw_first_page_from input do |output|
|
24
|
-
yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png"
|
24
|
+
yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png", **options
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -2,26 +2,33 @@
|
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
4
|
class Previewer::VideoPreviewer < Previewer
|
5
|
-
|
6
|
-
blob
|
5
|
+
class << self
|
6
|
+
def accept?(blob)
|
7
|
+
blob.video? && ffmpeg_exists?
|
8
|
+
end
|
9
|
+
|
10
|
+
def ffmpeg_exists?
|
11
|
+
return @ffmpeg_exists if defined?(@ffmpeg_exists)
|
12
|
+
|
13
|
+
@ffmpeg_exists = system(ffmpeg_path, "-version", out: File::NULL, err: File::NULL)
|
14
|
+
end
|
15
|
+
|
16
|
+
def ffmpeg_path
|
17
|
+
ActiveStorage.paths[:ffmpeg] || "ffmpeg"
|
18
|
+
end
|
7
19
|
end
|
8
20
|
|
9
|
-
def preview
|
21
|
+
def preview(**options)
|
10
22
|
download_blob_to_tempfile do |input|
|
11
23
|
draw_relevant_frame_from input do |output|
|
12
|
-
yield io: output, filename: "#{blob.filename.base}.
|
24
|
+
yield io: output, filename: "#{blob.filename.base}.jpg", content_type: "image/jpeg", **options
|
13
25
|
end
|
14
26
|
end
|
15
27
|
end
|
16
28
|
|
17
29
|
private
|
18
30
|
def draw_relevant_frame_from(file, &block)
|
19
|
-
draw ffmpeg_path, "-i", file.path,
|
20
|
-
"-vf", "thumbnail", "-vframes", "1", "-f", "image2", "-", &block
|
21
|
-
end
|
22
|
-
|
23
|
-
def ffmpeg_path
|
24
|
-
ActiveStorage.paths[:ffmpeg] || "ffmpeg"
|
31
|
+
draw self.class.ffmpeg_path, "-i", file.path, *Shellwords.split(ActiveStorage.video_preview_arguments), "-", &block
|
25
32
|
end
|
26
33
|
end
|
27
34
|
end
|
@@ -1,14 +1,10 @@
|
|
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 previewers, which generate images from blobs. See
|
7
5
|
# ActiveStorage::Previewer::MuPDFPreviewer and ActiveStorage::Previewer::VideoPreviewer for
|
8
6
|
# examples of concrete subclasses.
|
9
7
|
class Previewer
|
10
|
-
include Downloading
|
11
|
-
|
12
8
|
attr_reader :blob
|
13
9
|
|
14
10
|
# Implement this method in a concrete subclass. Have it return true when given a blob from which
|
@@ -22,15 +18,21 @@ module ActiveStorage
|
|
22
18
|
end
|
23
19
|
|
24
20
|
# Override this method in a concrete subclass. Have it yield an attachable preview image (i.e.
|
25
|
-
# anything accepted by ActiveStorage::Attached::One#attach).
|
26
|
-
|
21
|
+
# anything accepted by ActiveStorage::Attached::One#attach). Pass the additional options to
|
22
|
+
# the underlying blob that is created.
|
23
|
+
def preview(**options)
|
27
24
|
raise NotImplementedError
|
28
25
|
end
|
29
26
|
|
30
27
|
private
|
28
|
+
# Downloads the blob to a tempfile on disk. Yields the tempfile.
|
29
|
+
def download_blob_to_tempfile(&block) #:doc:
|
30
|
+
blob.open tmpdir: tmpdir, &block
|
31
|
+
end
|
32
|
+
|
31
33
|
# Executes a system command, capturing its binary output in a tempfile. Yields the tempfile.
|
32
34
|
#
|
33
|
-
# Use this method to shell out to a system library (e.g.
|
35
|
+
# Use this method to shell out to a system library (e.g. muPDF or FFmpeg) for preview image
|
34
36
|
# generation. The resulting tempfile can be used as the +:io+ value in an attachable Hash:
|
35
37
|
#
|
36
38
|
# def preview
|
@@ -41,18 +43,19 @@ module ActiveStorage
|
|
41
43
|
# end
|
42
44
|
# end
|
43
45
|
#
|
44
|
-
# The output tempfile is opened in the directory returned by
|
46
|
+
# The output tempfile is opened in the directory returned by #tmpdir.
|
45
47
|
def draw(*argv) #:doc:
|
46
|
-
|
47
|
-
|
48
|
+
open_tempfile do |file|
|
49
|
+
instrument :preview, key: blob.key do
|
48
50
|
capture(*argv, to: file)
|
49
|
-
yield file
|
50
51
|
end
|
52
|
+
|
53
|
+
yield file
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
54
|
-
def
|
55
|
-
tempfile = Tempfile.open("ActiveStorage",
|
57
|
+
def open_tempfile
|
58
|
+
tempfile = Tempfile.open("ActiveStorage-", tmpdir)
|
56
59
|
|
57
60
|
begin
|
58
61
|
yield tempfile
|
@@ -61,14 +64,31 @@ module ActiveStorage
|
|
61
64
|
end
|
62
65
|
end
|
63
66
|
|
67
|
+
def instrument(operation, payload = {}, &block)
|
68
|
+
ActiveSupport::Notifications.instrument "#{operation}.active_storage", payload, &block
|
69
|
+
end
|
70
|
+
|
64
71
|
def capture(*argv, to:)
|
65
72
|
to.binmode
|
66
|
-
|
73
|
+
|
74
|
+
open_tempfile do |err|
|
75
|
+
IO.popen(argv, err: err) { |out| IO.copy_stream(out, to) }
|
76
|
+
err.rewind
|
77
|
+
|
78
|
+
unless $?.success?
|
79
|
+
raise PreviewError, "#{argv.first} failed (status #{$?.exitstatus}): #{err.read.to_s.chomp}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
67
83
|
to.rewind
|
68
84
|
end
|
69
85
|
|
70
86
|
def logger #:doc:
|
71
87
|
ActiveStorage.logger
|
72
88
|
end
|
89
|
+
|
90
|
+
def tmpdir #:doc:
|
91
|
+
Dir.tmpdir
|
92
|
+
end
|
73
93
|
end
|
74
94
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveStorage
|
4
|
+
module Reflection
|
5
|
+
# Holds all the metadata about a has_one_attached attachment as it was
|
6
|
+
# specified in the Active Record class.
|
7
|
+
class HasOneAttachedReflection < ActiveRecord::Reflection::MacroReflection #:nodoc:
|
8
|
+
def macro
|
9
|
+
:has_one_attached
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Holds all the metadata about a has_many_attached attachment as it was
|
14
|
+
# specified in the Active Record class.
|
15
|
+
class HasManyAttachedReflection < ActiveRecord::Reflection::MacroReflection #:nodoc:
|
16
|
+
def macro
|
17
|
+
:has_many_attached
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module ReflectionExtension # :nodoc:
|
22
|
+
def add_attachment_reflection(model, name, reflection)
|
23
|
+
model.attachment_reflections = model.attachment_reflections.merge(name.to_s => reflection)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def reflection_class_for(macro)
|
28
|
+
case macro
|
29
|
+
when :has_one_attached
|
30
|
+
HasOneAttachedReflection
|
31
|
+
when :has_many_attached
|
32
|
+
HasManyAttachedReflection
|
33
|
+
else
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module ActiveRecordExtensions
|
40
|
+
extend ActiveSupport::Concern
|
41
|
+
|
42
|
+
included do
|
43
|
+
class_attribute :attachment_reflections, instance_writer: false, default: {}
|
44
|
+
end
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
# Returns an array of reflection objects for all the attachments in the
|
48
|
+
# class.
|
49
|
+
def reflect_on_all_attachments
|
50
|
+
attachment_reflections.values
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the reflection object for the named +attachment+.
|
54
|
+
#
|
55
|
+
# User.reflect_on_attachment(:avatar)
|
56
|
+
# # => the avatar reflection
|
57
|
+
#
|
58
|
+
def reflect_on_attachment(attachment)
|
59
|
+
attachment_reflections[attachment.to_s]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|