activestorage 7.0.7.2 → 7.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +140 -318
- data/MIT-LICENSE +1 -1
- data/README.md +4 -4
- data/app/assets/javascripts/activestorage.esm.js +8 -4
- data/app/assets/javascripts/activestorage.js +9 -3
- data/app/controllers/active_storage/blobs/proxy_controller.rb +1 -0
- data/app/controllers/active_storage/disk_controller.rb +4 -2
- data/app/controllers/active_storage/representations/proxy_controller.rb +1 -0
- data/app/controllers/concerns/active_storage/disable_session.rb +12 -0
- data/app/controllers/concerns/active_storage/file_server.rb +4 -1
- data/app/javascript/activestorage/blob_record.js +4 -1
- data/app/javascript/activestorage/direct_upload.js +3 -2
- data/app/javascript/activestorage/index.js +3 -1
- data/app/jobs/active_storage/transform_job.rb +12 -0
- data/app/models/active_storage/attachment.rb +87 -13
- data/app/models/active_storage/blob/analyzable.rb +4 -3
- data/app/models/active_storage/blob/identifiable.rb +1 -0
- data/app/models/active_storage/blob/representable.rb +7 -3
- data/app/models/active_storage/blob.rb +25 -45
- data/app/models/active_storage/current.rb +0 -10
- data/app/models/active_storage/filename.rb +2 -0
- data/app/models/active_storage/named_variant.rb +21 -0
- data/app/models/active_storage/preview.rb +5 -3
- data/app/models/active_storage/variant.rb +8 -7
- data/app/models/active_storage/variant_with_record.rb +19 -7
- data/app/models/active_storage/variation.rb +5 -3
- data/db/migrate/20170806125915_create_active_storage_tables.rb +1 -1
- data/lib/active_storage/analyzer/audio_analyzer.rb +16 -4
- data/lib/active_storage/analyzer/image_analyzer.rb +2 -0
- data/lib/active_storage/analyzer/video_analyzer.rb +3 -1
- data/lib/active_storage/analyzer.rb +2 -0
- data/lib/active_storage/attached/changes/create_many.rb +8 -3
- data/lib/active_storage/attached/changes/create_one.rb +14 -2
- data/lib/active_storage/attached/many.rb +5 -4
- data/lib/active_storage/attached/model.rb +66 -43
- data/lib/active_storage/attached/one.rb +5 -4
- data/lib/active_storage/attached.rb +2 -0
- data/lib/active_storage/deprecator.rb +7 -0
- data/lib/active_storage/engine.rb +11 -7
- data/lib/active_storage/fixture_set.rb +2 -0
- data/lib/active_storage/gem_version.rb +4 -4
- data/lib/active_storage/log_subscriber.rb +12 -0
- data/lib/active_storage/previewer.rb +8 -1
- data/lib/active_storage/reflection.rb +3 -3
- data/lib/active_storage/service/azure_storage_service.rb +2 -0
- data/lib/active_storage/service/disk_service.rb +2 -0
- data/lib/active_storage/service/gcs_service.rb +11 -20
- data/lib/active_storage/service/mirror_service.rb +10 -5
- data/lib/active_storage/service/s3_service.rb +2 -0
- data/lib/active_storage/service.rb +4 -2
- data/lib/active_storage/transformers/transformer.rb +2 -0
- data/lib/active_storage/version.rb +1 -1
- data/lib/active_storage.rb +19 -3
- metadata +20 -30
@@ -1,35 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# = Active Storage \Variant With Record
|
4
|
+
#
|
3
5
|
# Like an ActiveStorage::Variant, but keeps detail about the variant in the database as an
|
4
6
|
# ActiveStorage::VariantRecord. This is only used if +ActiveStorage.track_variants+ is enabled.
|
5
7
|
class ActiveStorage::VariantWithRecord
|
6
8
|
attr_reader :blob, :variation
|
7
9
|
delegate :service, to: :blob
|
10
|
+
delegate :content_type, to: :variation
|
8
11
|
|
9
12
|
def initialize(blob, variation)
|
10
13
|
@blob, @variation = blob, ActiveStorage::Variation.wrap(variation)
|
11
14
|
end
|
12
15
|
|
13
16
|
def processed
|
14
|
-
process
|
17
|
+
process unless processed?
|
15
18
|
self
|
16
19
|
end
|
17
20
|
|
18
|
-
def
|
19
|
-
|
21
|
+
def image
|
22
|
+
record&.image
|
20
23
|
end
|
21
24
|
|
22
|
-
def
|
23
|
-
|
25
|
+
def filename
|
26
|
+
ActiveStorage::Filename.new "#{blob.filename.base}.#{variation.format.downcase}"
|
24
27
|
end
|
25
28
|
|
26
|
-
|
27
|
-
|
29
|
+
# Destroys record and deletes file from service.
|
30
|
+
def destroy
|
31
|
+
record&.destroy
|
28
32
|
end
|
29
33
|
|
30
34
|
delegate :key, :url, :download, to: :image, allow_nil: true
|
31
35
|
|
32
36
|
private
|
37
|
+
def processed?
|
38
|
+
record.present?
|
39
|
+
end
|
40
|
+
|
41
|
+
def process
|
42
|
+
transform_blob { |image| create_or_find_record(image: image) }
|
43
|
+
end
|
44
|
+
|
33
45
|
def transform_blob
|
34
46
|
blob.open do |input|
|
35
47
|
variation.transform(input) do |output|
|
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "marcel"
|
4
4
|
|
5
|
+
# = Active Storage \Variation
|
6
|
+
#
|
5
7
|
# A set of transformations that can be applied to a blob to create a variant. This class is exposed via
|
6
8
|
# the ActiveStorage::Blob#variant method and should rarely be used directly.
|
7
9
|
#
|
@@ -59,14 +61,14 @@ class ActiveStorage::Variation
|
|
59
61
|
|
60
62
|
def format
|
61
63
|
transformations.fetch(:format, :png).tap do |format|
|
62
|
-
if
|
64
|
+
if Marcel::Magic.by_extension(format.to_s).nil?
|
63
65
|
raise ArgumentError, "Invalid variant format (#{format.inspect})"
|
64
66
|
end
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
68
70
|
def content_type
|
69
|
-
|
71
|
+
Marcel::MimeType.for(extension: format.to_s)
|
70
72
|
end
|
71
73
|
|
72
74
|
# Returns a signed key for all the +transformations+ that this variation was instantiated with.
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class CreateActiveStorageTables < ActiveRecord::Migration[
|
1
|
+
class CreateActiveStorageTables < ActiveRecord::Migration[7.0]
|
2
2
|
def change
|
3
3
|
# Use Active Record's configured type for primary and foreign keys
|
4
4
|
primary_key_type, foreign_key_type = primary_and_foreign_key_types
|
@@ -1,21 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
|
-
#
|
4
|
+
# = Active Storage Audio \Analyzer
|
5
|
+
#
|
6
|
+
# Extracts duration (seconds), bit_rate (bits/s), sample_rate (hertz) and tags (internal metadata) from an audio blob.
|
5
7
|
#
|
6
8
|
# Example:
|
7
9
|
#
|
8
10
|
# ActiveStorage::Analyzer::AudioAnalyzer.new(blob).metadata
|
9
|
-
# # => { duration: 5.0, bit_rate: 320340 }
|
11
|
+
# # => { duration: 5.0, bit_rate: 320340, sample_rate: 44100, tags: { encoder: "Lavc57.64", ... } }
|
10
12
|
#
|
11
|
-
# This analyzer requires the {FFmpeg}[https://www.ffmpeg.org] system library, which is not provided by Rails.
|
13
|
+
# This analyzer requires the {FFmpeg}[https://www.ffmpeg.org] system library, which is not provided by \Rails.
|
12
14
|
class Analyzer::AudioAnalyzer < Analyzer
|
13
15
|
def self.accept?(blob)
|
14
16
|
blob.audio?
|
15
17
|
end
|
16
18
|
|
17
19
|
def metadata
|
18
|
-
{ duration: duration, bit_rate: bit_rate }.compact
|
20
|
+
{ duration: duration, bit_rate: bit_rate, sample_rate: sample_rate, tags: tags }.compact
|
19
21
|
end
|
20
22
|
|
21
23
|
private
|
@@ -29,6 +31,16 @@ module ActiveStorage
|
|
29
31
|
Integer(bit_rate) if bit_rate
|
30
32
|
end
|
31
33
|
|
34
|
+
def sample_rate
|
35
|
+
sample_rate = audio_stream["sample_rate"]
|
36
|
+
Integer(sample_rate) if sample_rate
|
37
|
+
end
|
38
|
+
|
39
|
+
def tags
|
40
|
+
tags = audio_stream["tags"]
|
41
|
+
Hash(tags) if tags
|
42
|
+
end
|
43
|
+
|
32
44
|
def audio_stream
|
33
45
|
@audio_stream ||= streams.detect { |stream| stream["codec_type"] == "audio" } || {}
|
34
46
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
|
+
# = Active Storage Image \Analyzer
|
5
|
+
#
|
4
6
|
# This is an abstract base class for image analyzers, which extract width and height from an image blob.
|
5
7
|
#
|
6
8
|
# If the image contains EXIF data indicating its angle is 90 or 270 degrees, its width and height are swapped for convenience.
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
|
+
# = Active Storage Video \Analyzer
|
5
|
+
#
|
4
6
|
# Extracts the following from a video blob:
|
5
7
|
#
|
6
8
|
# * Width (pixels)
|
@@ -18,7 +20,7 @@ module ActiveStorage
|
|
18
20
|
#
|
19
21
|
# When a video's angle is 90, -90, 270 or -270 degrees, its width and height are automatically swapped for convenience.
|
20
22
|
#
|
21
|
-
# This analyzer requires the {FFmpeg}[https://www.ffmpeg.org] system library, which is not provided by Rails.
|
23
|
+
# This analyzer requires the {FFmpeg}[https://www.ffmpeg.org] system library, which is not provided by \Rails.
|
22
24
|
class Analyzer::VideoAnalyzer < Analyzer
|
23
25
|
def self.accept?(blob)
|
24
26
|
blob.video?
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
|
+
# = Active Storage \Analyzer
|
5
|
+
#
|
4
6
|
# This is an abstract base class for analyzers, which extract metadata from blobs. See
|
5
7
|
# ActiveStorage::Analyzer::VideoAnalyzer for an example of a concrete subclass.
|
6
8
|
class Analyzer
|
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
4
|
class Attached::Changes::CreateMany # :nodoc:
|
5
|
-
attr_reader :name, :record, :attachables
|
5
|
+
attr_reader :name, :record, :attachables, :pending_uploads
|
6
6
|
|
7
|
-
def initialize(name, record, attachables)
|
7
|
+
def initialize(name, record, attachables, pending_uploads: [])
|
8
8
|
@name, @record, @attachables = name, record, Array(attachables)
|
9
9
|
blobs.each(&:identify_without_saving)
|
10
|
+
@pending_uploads = Array(pending_uploads) + subchanges_without_blobs
|
10
11
|
attachments
|
11
12
|
end
|
12
13
|
|
@@ -19,7 +20,7 @@ module ActiveStorage
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def upload
|
22
|
-
|
23
|
+
pending_uploads.each(&:upload)
|
23
24
|
end
|
24
25
|
|
25
26
|
def save
|
@@ -36,6 +37,10 @@ module ActiveStorage
|
|
36
37
|
ActiveStorage::Attached::Changes::CreateOneOfMany.new(name, record, attachable)
|
37
38
|
end
|
38
39
|
|
40
|
+
def subchanges_without_blobs
|
41
|
+
subchanges.reject { |subchange| subchange.attachable.is_a?(ActiveStorage::Blob) }
|
42
|
+
end
|
43
|
+
|
39
44
|
def assign_associated_attachments
|
40
45
|
record.public_send("#{name}_attachments=", persisted_or_new_attachments)
|
41
46
|
end
|
@@ -22,8 +22,12 @@ module ActiveStorage
|
|
22
22
|
|
23
23
|
def upload
|
24
24
|
case attachable
|
25
|
-
when ActionDispatch::Http::UploadedFile
|
25
|
+
when ActionDispatch::Http::UploadedFile
|
26
26
|
blob.upload_without_unfurling(attachable.open)
|
27
|
+
when Rack::Test::UploadedFile
|
28
|
+
blob.upload_without_unfurling(
|
29
|
+
attachable.respond_to?(:open) ? attachable.open : attachable
|
30
|
+
)
|
27
31
|
when Hash
|
28
32
|
blob.upload_without_unfurling(attachable.fetch(:io))
|
29
33
|
end
|
@@ -53,7 +57,7 @@ module ActiveStorage
|
|
53
57
|
case attachable
|
54
58
|
when ActiveStorage::Blob
|
55
59
|
attachable
|
56
|
-
when ActionDispatch::Http::UploadedFile
|
60
|
+
when ActionDispatch::Http::UploadedFile
|
57
61
|
ActiveStorage::Blob.build_after_unfurling(
|
58
62
|
io: attachable.open,
|
59
63
|
filename: attachable.original_filename,
|
@@ -61,6 +65,14 @@ module ActiveStorage
|
|
61
65
|
record: record,
|
62
66
|
service_name: attachment_service_name
|
63
67
|
)
|
68
|
+
when Rack::Test::UploadedFile
|
69
|
+
ActiveStorage::Blob.build_after_unfurling(
|
70
|
+
io: attachable.respond_to?(:open) ? attachable.open : attachable,
|
71
|
+
filename: attachable.original_filename,
|
72
|
+
content_type: attachable.content_type,
|
73
|
+
record: record,
|
74
|
+
service_name: attachment_service_name
|
75
|
+
)
|
64
76
|
when Hash
|
65
77
|
ActiveStorage::Blob.build_after_unfurling(
|
66
78
|
**attachable.reverse_merge(
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
|
+
# = Active Storage \Attached \Many
|
5
|
+
#
|
4
6
|
# Decorated proxy object representing of multiple attachments to a model.
|
5
7
|
class Attached::Many < Attached
|
6
8
|
##
|
@@ -47,12 +49,11 @@ module ActiveStorage
|
|
47
49
|
# document.images.attach(io: File.open("/path/to/racecar.jpg"), filename: "racecar.jpg", content_type: "image/jpeg")
|
48
50
|
# document.images.attach([ first_blob, second_blob ])
|
49
51
|
def attach(*attachables)
|
52
|
+
record.public_send("#{name}=", blobs + attachables.flatten)
|
50
53
|
if record.persisted? && !record.changed?
|
51
|
-
|
52
|
-
record.save
|
53
|
-
else
|
54
|
-
record.public_send("#{name}=", (change&.attachables || blobs) + attachables.flatten)
|
54
|
+
return if !record.save
|
55
55
|
end
|
56
|
+
record.public_send("#{name}")
|
56
57
|
end
|
57
58
|
|
58
59
|
# Returns true if any attachments have been made.
|
@@ -3,10 +3,54 @@
|
|
3
3
|
require "active_support/core_ext/object/try"
|
4
4
|
|
5
5
|
module ActiveStorage
|
6
|
+
# = Active Storage \Attached \Model
|
7
|
+
#
|
6
8
|
# Provides the class-level DSL for declaring an Active Record model's attachments.
|
7
9
|
module Attached::Model
|
8
10
|
extend ActiveSupport::Concern
|
9
11
|
|
12
|
+
##
|
13
|
+
# :method: *_attachment
|
14
|
+
#
|
15
|
+
# Returns the attachment for the +has_one_attached+.
|
16
|
+
#
|
17
|
+
# User.last.avatar_attachment
|
18
|
+
|
19
|
+
##
|
20
|
+
# :method: *_attachments
|
21
|
+
#
|
22
|
+
# Returns the attachments for the +has_many_attached+.
|
23
|
+
#
|
24
|
+
# Gallery.last.photos_attachments
|
25
|
+
|
26
|
+
##
|
27
|
+
# :method: *_blob
|
28
|
+
#
|
29
|
+
# Returns the blob for the +has_one_attached+ attachment.
|
30
|
+
#
|
31
|
+
# User.last.avatar_blob
|
32
|
+
|
33
|
+
##
|
34
|
+
# :method: *_blobs
|
35
|
+
#
|
36
|
+
# Returns the blobs for the +has_many_attached+ attachments.
|
37
|
+
#
|
38
|
+
# Gallery.last.photos_blobs
|
39
|
+
|
40
|
+
##
|
41
|
+
# :method: with_attached_*
|
42
|
+
#
|
43
|
+
# Includes the attached blobs in your query to avoid N+1 queries.
|
44
|
+
#
|
45
|
+
# If +ActiveStorage.track_variants+ is enabled, it will also include the
|
46
|
+
# variants record and their attached blobs.
|
47
|
+
#
|
48
|
+
# User.with_attached_avatar
|
49
|
+
#
|
50
|
+
# Use the plural form for +has_many_attached+:
|
51
|
+
#
|
52
|
+
# Gallery.with_attached_photos
|
53
|
+
|
10
54
|
class_methods do
|
11
55
|
# Specifies the relation between a single attachment and the model.
|
12
56
|
#
|
@@ -59,7 +103,7 @@ module ActiveStorage
|
|
59
103
|
|
60
104
|
def #{name}=(attachable)
|
61
105
|
attachment_changes["#{name}"] =
|
62
|
-
if attachable.nil?
|
106
|
+
if attachable.nil? || attachable == ""
|
63
107
|
ActiveStorage::Attached::Changes::DeleteOne.new("#{name}", self)
|
64
108
|
else
|
65
109
|
ActiveStorage::Attached::Changes::CreateOne.new("#{name}", self, attachable)
|
@@ -70,7 +114,13 @@ module ActiveStorage
|
|
70
114
|
has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record, inverse_of: :record, dependent: :destroy, strict_loading: strict_loading
|
71
115
|
has_one :"#{name}_blob", through: :"#{name}_attachment", class_name: "ActiveStorage::Blob", source: :blob, strict_loading: strict_loading
|
72
116
|
|
73
|
-
scope :"with_attached_#{name}", -> {
|
117
|
+
scope :"with_attached_#{name}", -> {
|
118
|
+
if ActiveStorage.track_variants
|
119
|
+
includes("#{name}_attachment": { blob: { variant_records: { image_attachment: :blob } } })
|
120
|
+
else
|
121
|
+
includes("#{name}_attachment": :blob)
|
122
|
+
end
|
123
|
+
}
|
74
124
|
|
75
125
|
after_save { attachment_changes[name.to_s]&.save }
|
76
126
|
|
@@ -138,57 +188,22 @@ module ActiveStorage
|
|
138
188
|
|
139
189
|
def #{name}=(attachables)
|
140
190
|
attachables = Array(attachables).compact_blank
|
191
|
+
pending_uploads = attachment_changes["#{name}"].try(:pending_uploads)
|
141
192
|
|
142
|
-
if
|
143
|
-
|
144
|
-
if attachables.none?
|
145
|
-
ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
|
146
|
-
else
|
147
|
-
ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables)
|
148
|
-
end
|
193
|
+
attachment_changes["#{name}"] = if attachables.none?
|
194
|
+
ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
|
149
195
|
else
|
150
|
-
|
151
|
-
"config.active_storage.replace_on_assign_to_many is deprecated and will be removed in Rails 7.1. " \
|
152
|
-
"Make sure that your code works well with config.active_storage.replace_on_assign_to_many set to true before upgrading. " \
|
153
|
-
"To append new attachables to the Active Storage association, prefer using `attach`. " \
|
154
|
-
"Using association setter would result in purging the existing attached attachments and replacing them with new ones."
|
155
|
-
|
156
|
-
if attachables.any?
|
157
|
-
attachment_changes["#{name}"] =
|
158
|
-
ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, #{name}.blobs + attachables)
|
159
|
-
end
|
196
|
+
ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables, pending_uploads: pending_uploads)
|
160
197
|
end
|
161
198
|
end
|
162
199
|
CODE
|
163
200
|
|
164
|
-
has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record, dependent: :destroy, strict_loading: strict_loading
|
165
|
-
def purge
|
166
|
-
deprecate(:purge)
|
167
|
-
each(&:purge)
|
168
|
-
reset
|
169
|
-
end
|
170
|
-
|
171
|
-
def purge_later
|
172
|
-
deprecate(:purge_later)
|
173
|
-
each(&:purge_later)
|
174
|
-
reset
|
175
|
-
end
|
176
|
-
|
177
|
-
private
|
178
|
-
def deprecate(action)
|
179
|
-
reflection_name = proxy_association.reflection.name
|
180
|
-
attached_name = reflection_name.to_s.partition("_").first
|
181
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
182
|
-
Calling `#{action}` from `#{reflection_name}` is deprecated and will be removed in Rails 7.1.
|
183
|
-
To migrate to Rails 7.1's behavior call `#{action}` from `#{attached_name}` instead: `#{attached_name}.#{action}`.
|
184
|
-
MSG
|
185
|
-
end
|
186
|
-
end
|
201
|
+
has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record, dependent: :destroy, strict_loading: strict_loading
|
187
202
|
has_many :"#{name}_blobs", through: :"#{name}_attachments", class_name: "ActiveStorage::Blob", source: :blob, strict_loading: strict_loading
|
188
203
|
|
189
204
|
scope :"with_attached_#{name}", -> {
|
190
205
|
if ActiveStorage.track_variants
|
191
|
-
includes("#{name}_attachments": { blob: :
|
206
|
+
includes("#{name}_attachments": { blob: { variant_records: { image_attachment: :blob } } })
|
192
207
|
else
|
193
208
|
includes("#{name}_attachments": :blob)
|
194
209
|
end
|
@@ -215,6 +230,14 @@ module ActiveStorage
|
|
215
230
|
ActiveStorage::Blob.services.fetch(service) do
|
216
231
|
raise ArgumentError, "Cannot configure service :#{service} for #{name}##{association_name}"
|
217
232
|
end
|
233
|
+
else
|
234
|
+
validate_global_service_configuration
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def validate_global_service_configuration
|
239
|
+
if connected? && ActiveStorage::Blob.table_exists? && Rails.configuration.active_storage.service.nil?
|
240
|
+
raise RuntimeError, "Missing Active Storage service name. Specify Active Storage service name for config.active_storage.service in config/environments/#{Rails.env}.rb"
|
218
241
|
end
|
219
242
|
end
|
220
243
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
|
+
# = Active Storage \Attached \One
|
5
|
+
#
|
4
6
|
# Representation of a single attachment to a model.
|
5
7
|
class Attached::One < Attached
|
6
8
|
##
|
@@ -54,12 +56,11 @@ module ActiveStorage
|
|
54
56
|
# person.avatar.attach(io: File.open("/path/to/face.jpg"), filename: "face.jpg", content_type: "image/jpeg")
|
55
57
|
# person.avatar.attach(avatar_blob) # ActiveStorage::Blob object
|
56
58
|
def attach(attachable)
|
59
|
+
record.public_send("#{name}=", attachable)
|
57
60
|
if record.persisted? && !record.changed?
|
58
|
-
record.
|
59
|
-
record.save
|
60
|
-
else
|
61
|
-
record.public_send("#{name}=", attachable)
|
61
|
+
return if !record.save
|
62
62
|
end
|
63
|
+
record.public_send("#{name}")
|
63
64
|
end
|
64
65
|
|
65
66
|
# Returns +true+ if an attachment has been made.
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require "active_support/core_ext/module/delegation"
|
4
4
|
|
5
5
|
module ActiveStorage
|
6
|
+
# = Active Storage \Attached
|
7
|
+
#
|
6
8
|
# Abstract base class for the concrete ActiveStorage::Attached::One and ActiveStorage::Attached::Many
|
7
9
|
# classes that both provide proxy access to the blob association for a record.
|
8
10
|
class Attached
|
@@ -35,9 +35,7 @@ module ActiveStorage
|
|
35
35
|
config.active_storage.variable_content_types = %w(
|
36
36
|
image/png
|
37
37
|
image/gif
|
38
|
-
image/jpg
|
39
38
|
image/jpeg
|
40
|
-
image/pjpeg
|
41
39
|
image/tiff
|
42
40
|
image/bmp
|
43
41
|
image/vnd.adobe.photoshop
|
@@ -51,13 +49,11 @@ module ActiveStorage
|
|
51
49
|
config.active_storage.web_image_content_types = %w(
|
52
50
|
image/png
|
53
51
|
image/jpeg
|
54
|
-
image/jpg
|
55
52
|
image/gif
|
56
53
|
)
|
57
54
|
|
58
55
|
config.active_storage.content_types_to_serve_as_binary = %w(
|
59
56
|
text/html
|
60
|
-
text/javascript
|
61
57
|
image/svg+xml
|
62
58
|
application/postscript
|
63
59
|
application/x-shockwave-flash
|
@@ -71,7 +67,6 @@ module ActiveStorage
|
|
71
67
|
config.active_storage.content_types_allowed_inline = %w(
|
72
68
|
image/png
|
73
69
|
image/gif
|
74
|
-
image/jpg
|
75
70
|
image/jpeg
|
76
71
|
image/tiff
|
77
72
|
image/bmp
|
@@ -82,6 +77,10 @@ module ActiveStorage
|
|
82
77
|
|
83
78
|
config.eager_load_namespaces << ActiveStorage
|
84
79
|
|
80
|
+
initializer "active_storage.deprecator", before: :load_environment_config do |app|
|
81
|
+
app.deprecators[:active_storage] = ActiveStorage.deprecator
|
82
|
+
end
|
83
|
+
|
85
84
|
initializer "active_storage.configs" do
|
86
85
|
config.after_initialize do |app|
|
87
86
|
ActiveStorage.logger = app.config.active_storage.logger || Rails.logger
|
@@ -117,9 +116,14 @@ module ActiveStorage
|
|
117
116
|
ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream"
|
118
117
|
ActiveStorage.video_preview_arguments = app.config.active_storage.video_preview_arguments || "-y -vframes 1 -f image2"
|
119
118
|
|
120
|
-
|
119
|
+
unless app.config.active_storage.silence_invalid_content_types_warning.nil?
|
120
|
+
ActiveStorage.silence_invalid_content_types_warning = app.config.active_storage.silence_invalid_content_types_warning
|
121
|
+
end
|
122
|
+
|
123
|
+
unless app.config.active_storage.replace_on_assign_to_many.nil?
|
124
|
+
ActiveStorage.replace_on_assign_to_many = app.config.active_storage.replace_on_assign_to_many
|
125
|
+
end
|
121
126
|
|
122
|
-
ActiveStorage.replace_on_assign_to_many = app.config.active_storage.replace_on_assign_to_many || false
|
123
127
|
ActiveStorage.track_variants = app.config.active_storage.track_variants || false
|
124
128
|
end
|
125
129
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
|
-
# Returns the currently loaded version of Active Storage as a
|
4
|
+
# Returns the currently loaded version of Active Storage as a +Gem::Version+.
|
5
5
|
def self.gem_version
|
6
6
|
Gem::Version.new VERSION::STRING
|
7
7
|
end
|
8
8
|
|
9
9
|
module VERSION
|
10
10
|
MAJOR = 7
|
11
|
-
MINOR =
|
12
|
-
TINY =
|
13
|
-
PRE = "
|
11
|
+
MINOR = 1
|
12
|
+
TINY = 0
|
13
|
+
PRE = "beta1"
|
14
14
|
|
15
15
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
16
16
|
end
|
@@ -9,34 +9,46 @@ module ActiveStorage
|
|
9
9
|
message += " (checksum: #{event.payload[:checksum]})" if event.payload[:checksum]
|
10
10
|
info event, color(message, GREEN)
|
11
11
|
end
|
12
|
+
subscribe_log_level :service_upload, :info
|
12
13
|
|
13
14
|
def service_download(event)
|
14
15
|
info event, color("Downloaded file from key: #{key_in(event)}", BLUE)
|
15
16
|
end
|
17
|
+
subscribe_log_level :service_download, :info
|
16
18
|
|
17
19
|
alias_method :service_streaming_download, :service_download
|
18
20
|
|
21
|
+
def preview(event)
|
22
|
+
info event, color("Previewed file from key: #{key_in(event)}", BLUE)
|
23
|
+
end
|
24
|
+
subscribe_log_level :preview, :info
|
25
|
+
|
19
26
|
def service_delete(event)
|
20
27
|
info event, color("Deleted file from key: #{key_in(event)}", RED)
|
21
28
|
end
|
29
|
+
subscribe_log_level :service_delete, :info
|
22
30
|
|
23
31
|
def service_delete_prefixed(event)
|
24
32
|
info event, color("Deleted files by key prefix: #{event.payload[:prefix]}", RED)
|
25
33
|
end
|
34
|
+
subscribe_log_level :service_delete_prefixed, :info
|
26
35
|
|
27
36
|
def service_exist(event)
|
28
37
|
debug event, color("Checked if file exists at key: #{key_in(event)} (#{event.payload[:exist] ? "yes" : "no"})", BLUE)
|
29
38
|
end
|
39
|
+
subscribe_log_level :service_exist, :debug
|
30
40
|
|
31
41
|
def service_url(event)
|
32
42
|
debug event, color("Generated URL for file at key: #{key_in(event)} (#{event.payload[:url]})", BLUE)
|
33
43
|
end
|
44
|
+
subscribe_log_level :service_url, :debug
|
34
45
|
|
35
46
|
def service_mirror(event)
|
36
47
|
message = "Mirrored file at key: #{key_in(event)}"
|
37
48
|
message += " (checksum: #{event.payload[:checksum]})" if event.payload[:checksum]
|
38
49
|
debug event, color(message, GREEN)
|
39
50
|
end
|
51
|
+
subscribe_log_level :service_mirror, :debug
|
40
52
|
|
41
53
|
def logger
|
42
54
|
ActiveStorage.logger
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveStorage
|
4
|
+
# = Active Storage \Previewer
|
5
|
+
#
|
4
6
|
# This is an abstract base class for previewers, which generate images from blobs. See
|
5
7
|
# ActiveStorage::Previewer::MuPDFPreviewer and ActiveStorage::Previewer::VideoPreviewer for
|
6
8
|
# examples of concrete subclasses.
|
@@ -65,7 +67,12 @@ module ActiveStorage
|
|
65
67
|
end
|
66
68
|
|
67
69
|
def instrument(operation, payload = {}, &block)
|
68
|
-
ActiveSupport::Notifications.instrument "#{operation}.active_storage", payload, &block
|
70
|
+
ActiveSupport::Notifications.instrument "#{operation}.active_storage", payload.merge(service: service_name), &block
|
71
|
+
end
|
72
|
+
|
73
|
+
def service_name
|
74
|
+
# ActiveStorage::Service::DiskService => Disk
|
75
|
+
blob.service.class.to_s.split("::").third.remove("Service")
|
69
76
|
end
|
70
77
|
|
71
78
|
def capture(*argv, to:)
|
@@ -4,11 +4,11 @@ module ActiveStorage
|
|
4
4
|
module Reflection
|
5
5
|
class HasAttachedReflection < ActiveRecord::Reflection::MacroReflection # :nodoc:
|
6
6
|
def variant(name, transformations)
|
7
|
-
|
7
|
+
named_variants[name] = NamedVariant.new(transformations)
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
@
|
10
|
+
def named_variants
|
11
|
+
@named_variants ||= {}
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -7,6 +7,8 @@ require "azure/storage/blob"
|
|
7
7
|
require "azure/storage/common/core/auth/shared_access_signature"
|
8
8
|
|
9
9
|
module ActiveStorage
|
10
|
+
# = Active Storage \Azure Storage \Service
|
11
|
+
#
|
10
12
|
# Wraps the Microsoft Azure Storage Blob Service as an Active Storage service.
|
11
13
|
# See ActiveStorage::Service for the generic API documentation that applies to all services.
|
12
14
|
class Service::AzureStorageService < Service
|
@@ -6,6 +6,8 @@ require "openssl"
|
|
6
6
|
require "active_support/core_ext/numeric/bytes"
|
7
7
|
|
8
8
|
module ActiveStorage
|
9
|
+
# = Active Storage \Disk \Service
|
10
|
+
#
|
9
11
|
# Wraps a local disk path as an Active Storage service. See ActiveStorage::Service for the generic API
|
10
12
|
# documentation that applies to all services.
|
11
13
|
class Service::DiskService < Service
|