activestorage 7.0.4 → 7.1.3.4

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +197 -213
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +7 -7
  5. data/app/assets/javascripts/activestorage.esm.js +11 -7
  6. data/app/assets/javascripts/activestorage.js +12 -6
  7. data/app/controllers/active_storage/blobs/proxy_controller.rb +1 -0
  8. data/app/controllers/active_storage/disk_controller.rb +4 -2
  9. data/app/controllers/active_storage/representations/proxy_controller.rb +2 -1
  10. data/app/controllers/concerns/active_storage/disable_session.rb +12 -0
  11. data/app/controllers/concerns/active_storage/file_server.rb +4 -1
  12. data/app/javascript/activestorage/blob_record.js +4 -1
  13. data/app/javascript/activestorage/direct_upload.js +3 -2
  14. data/app/javascript/activestorage/index.js +3 -1
  15. data/app/javascript/activestorage/ujs.js +3 -3
  16. data/app/jobs/active_storage/analyze_job.rb +1 -1
  17. data/app/jobs/active_storage/mirror_job.rb +1 -1
  18. data/app/jobs/active_storage/purge_job.rb +1 -1
  19. data/app/jobs/active_storage/transform_job.rb +12 -0
  20. data/app/models/active_storage/attachment.rb +91 -16
  21. data/app/models/active_storage/blob/analyzable.rb +4 -3
  22. data/app/models/active_storage/blob/identifiable.rb +1 -0
  23. data/app/models/active_storage/blob/representable.rb +7 -3
  24. data/app/models/active_storage/blob/servable.rb +22 -0
  25. data/app/models/active_storage/blob.rb +32 -68
  26. data/app/models/active_storage/current.rb +0 -10
  27. data/app/models/active_storage/filename.rb +2 -0
  28. data/app/models/active_storage/named_variant.rb +21 -0
  29. data/app/models/active_storage/preview.rb +11 -4
  30. data/app/models/active_storage/variant.rb +10 -7
  31. data/app/models/active_storage/variant_record.rb +0 -2
  32. data/app/models/active_storage/variant_with_record.rb +21 -7
  33. data/app/models/active_storage/variation.rb +5 -3
  34. data/config/routes.rb +6 -4
  35. data/db/migrate/20170806125915_create_active_storage_tables.rb +1 -1
  36. data/lib/active_storage/analyzer/audio_analyzer.rb +16 -4
  37. data/lib/active_storage/analyzer/image_analyzer.rb +2 -0
  38. data/lib/active_storage/analyzer/video_analyzer.rb +14 -5
  39. data/lib/active_storage/analyzer.rb +2 -0
  40. data/lib/active_storage/attached/changes/create_many.rb +8 -3
  41. data/lib/active_storage/attached/changes/create_one.rb +45 -3
  42. data/lib/active_storage/attached/many.rb +5 -4
  43. data/lib/active_storage/attached/model.rb +72 -43
  44. data/lib/active_storage/attached/one.rb +5 -4
  45. data/lib/active_storage/attached.rb +2 -0
  46. data/lib/active_storage/deprecator.rb +7 -0
  47. data/lib/active_storage/engine.rb +11 -7
  48. data/lib/active_storage/fixture_set.rb +12 -9
  49. data/lib/active_storage/gem_version.rb +4 -4
  50. data/lib/active_storage/log_subscriber.rb +12 -0
  51. data/lib/active_storage/previewer.rb +8 -1
  52. data/lib/active_storage/reflection.rb +3 -3
  53. data/lib/active_storage/service/azure_storage_service.rb +2 -0
  54. data/lib/active_storage/service/disk_service.rb +2 -0
  55. data/lib/active_storage/service/gcs_service.rb +11 -20
  56. data/lib/active_storage/service/mirror_service.rb +10 -5
  57. data/lib/active_storage/service/s3_service.rb +2 -0
  58. data/lib/active_storage/service.rb +4 -2
  59. data/lib/active_storage/transformers/transformer.rb +2 -0
  60. data/lib/active_storage/version.rb +1 -1
  61. data/lib/active_storage.rb +19 -3
  62. metadata +19 -28
@@ -22,10 +22,26 @@ module ActiveStorage
22
22
 
23
23
  def upload
24
24
  case attachable
25
- when ActionDispatch::Http::UploadedFile, Rack::Test::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))
33
+ when File
34
+ blob.upload_without_unfurling(attachable)
35
+ when Pathname
36
+ blob.upload_without_unfurling(attachable.open)
37
+ when ActiveStorage::Blob
38
+ when String
39
+ else
40
+ raise(
41
+ ArgumentError,
42
+ "Could not upload: expected attachable, " \
43
+ "got #{attachable.inspect}"
44
+ )
29
45
  end
30
46
  end
31
47
 
@@ -53,7 +69,7 @@ module ActiveStorage
53
69
  case attachable
54
70
  when ActiveStorage::Blob
55
71
  attachable
56
- when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
72
+ when ActionDispatch::Http::UploadedFile
57
73
  ActiveStorage::Blob.build_after_unfurling(
58
74
  io: attachable.open,
59
75
  filename: attachable.original_filename,
@@ -61,6 +77,14 @@ module ActiveStorage
61
77
  record: record,
62
78
  service_name: attachment_service_name
63
79
  )
80
+ when Rack::Test::UploadedFile
81
+ ActiveStorage::Blob.build_after_unfurling(
82
+ io: attachable.respond_to?(:open) ? attachable.open : attachable,
83
+ filename: attachable.original_filename,
84
+ content_type: attachable.content_type,
85
+ record: record,
86
+ service_name: attachment_service_name
87
+ )
64
88
  when Hash
65
89
  ActiveStorage::Blob.build_after_unfurling(
66
90
  **attachable.reverse_merge(
@@ -70,8 +94,26 @@ module ActiveStorage
70
94
  )
71
95
  when String
72
96
  ActiveStorage::Blob.find_signed!(attachable, record: record)
97
+ when File
98
+ ActiveStorage::Blob.build_after_unfurling(
99
+ io: attachable,
100
+ filename: File.basename(attachable),
101
+ record: record,
102
+ service_name: attachment_service_name
103
+ )
104
+ when Pathname
105
+ ActiveStorage::Blob.build_after_unfurling(
106
+ io: attachable.open,
107
+ filename: File.basename(attachable),
108
+ record: record,
109
+ service_name: attachment_service_name
110
+ )
73
111
  else
74
- raise ArgumentError, "Could not find or build blob: expected attachable, got #{attachable.inspect}"
112
+ raise(
113
+ ArgumentError,
114
+ "Could not find or build blob: expected attachable, " \
115
+ "got #{attachable.inspect}"
116
+ )
75
117
  end
76
118
  end
77
119
 
@@ -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
- record.public_send("#{name}=", blobs + attachables.flatten)
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,16 @@ 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}", -> { includes("#{name}_attachment": :blob) }
117
+ scope :"with_attached_#{name}", -> {
118
+ if ActiveStorage.track_variants
119
+ includes("#{name}_attachment": { blob: {
120
+ variant_records: { image_attachment: :blob },
121
+ preview_image_attachment: { blob: { variant_records: { image_attachment: :blob } } }
122
+ } })
123
+ else
124
+ includes("#{name}_attachment": :blob)
125
+ end
126
+ }
74
127
 
75
128
  after_save { attachment_changes[name.to_s]&.save }
76
129
 
@@ -138,57 +191,25 @@ module ActiveStorage
138
191
 
139
192
  def #{name}=(attachables)
140
193
  attachables = Array(attachables).compact_blank
194
+ pending_uploads = attachment_changes["#{name}"].try(:pending_uploads)
141
195
 
142
- if ActiveStorage.replace_on_assign_to_many
143
- attachment_changes["#{name}"] =
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
196
+ attachment_changes["#{name}"] = if attachables.none?
197
+ ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
149
198
  else
150
- ActiveSupport::Deprecation.warn \
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
199
+ ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables, pending_uploads: pending_uploads)
160
200
  end
161
201
  end
162
202
  CODE
163
203
 
164
- has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record, dependent: :destroy, strict_loading: strict_loading do
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
204
+ has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record, dependent: :destroy, strict_loading: strict_loading
187
205
  has_many :"#{name}_blobs", through: :"#{name}_attachments", class_name: "ActiveStorage::Blob", source: :blob, strict_loading: strict_loading
188
206
 
189
207
  scope :"with_attached_#{name}", -> {
190
208
  if ActiveStorage.track_variants
191
- includes("#{name}_attachments": { blob: :variant_records })
209
+ includes("#{name}_attachments": { blob: {
210
+ variant_records: { image_attachment: :blob },
211
+ preview_image_attachment: { blob: { variant_records: { image_attachment: :blob } } }
212
+ } })
192
213
  else
193
214
  includes("#{name}_attachments": :blob)
194
215
  end
@@ -215,6 +236,14 @@ module ActiveStorage
215
236
  ActiveStorage::Blob.services.fetch(service) do
216
237
  raise ArgumentError, "Cannot configure service :#{service} for #{name}##{association_name}"
217
238
  end
239
+ else
240
+ validate_global_service_configuration
241
+ end
242
+ end
243
+
244
+ def validate_global_service_configuration
245
+ if connected? && ActiveStorage::Blob.table_exists? && Rails.configuration.active_storage.service.nil?
246
+ raise RuntimeError, "Missing Active Storage service name. Specify Active Storage service name for config.active_storage.service in config/environments/#{Rails.env}.rb"
218
247
  end
219
248
  end
220
249
  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.public_send("#{name}=", attachable)
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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorage
4
+ def self.deprecator # :nodoc:
5
+ @deprecator ||= ActiveSupport::Deprecation.new
6
+ end
7
+ end
@@ -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
- ActiveStorage.silence_invalid_content_types_warning = app.config.active_storage.silence_invalid_content_types_warning || false
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
@@ -4,19 +4,18 @@ require "active_support/testing/file_fixtures"
4
4
  require "active_record/secure_token"
5
5
 
6
6
  module ActiveStorage
7
+ # = Active Storage \FixtureSet
8
+ #
7
9
  # Fixtures are a way of organizing data that you want to test against; in
8
10
  # short, sample data.
9
11
  #
10
- # To learn more about fixtures, read the
11
- # {ActiveRecord::FixtureSet}[rdoc-ref:ActiveRecord::FixtureSet] documentation.
12
+ # To learn more about fixtures, read the ActiveRecord::FixtureSet documentation.
12
13
  #
13
14
  # === YAML
14
15
  #
15
- # Like other Active Record-backed models,
16
- # {ActiveStorage::Attachment}[rdoc-ref:ActiveStorage::Attachment] and
17
- # {ActiveStorage::Blob}[rdoc-ref:ActiveStorage::Blob] records inherit from
18
- # {ActiveRecord::Base}[rdoc-ref:ActiveRecord::Base] instances and therefore
19
- # can be populated by fixtures.
16
+ # Like other Active Record-backed models, ActiveStorage::Attachment and
17
+ # ActiveStorage::Blob records inherit from ActiveRecord::Base instances and
18
+ # therefore can be populated by fixtures.
20
19
  #
21
20
  # Consider a hypothetical <tt>Article</tt> model class, its related
22
21
  # fixture data, as well as fixture data for related ActiveStorage::Attachment
@@ -27,9 +26,13 @@ module ActiveStorage
27
26
  # has_one_attached :thumbnail
28
27
  # end
29
28
  #
29
+ # <code></code>
30
+ #
30
31
  # # fixtures/active_storage/blobs.yml
31
32
  # first_thumbnail_blob: <%= ActiveStorage::FixtureSet.blob filename: "first.png" %>
32
33
  #
34
+ # <code></code>
35
+ #
33
36
  # # fixtures/active_storage/attachments.yml
34
37
  # first_thumbnail_attachment:
35
38
  # name: thumbnail
@@ -44,12 +47,12 @@ module ActiveStorage
44
47
 
45
48
  # Generate a YAML-encoded representation of an ActiveStorage::Blob
46
49
  # instance's attributes, resolve the file relative to the directory mentioned
47
- # by <tt>ActiveSupport::Testing::FileFixtures.file_fixture</tt>, and upload
50
+ # by ActiveSupport::Testing::FileFixtures.file_fixture, and upload
48
51
  # the file to the Service
49
52
  #
50
53
  # === Examples
51
54
  #
52
- # # tests/fixtures/action_text/blobs.yml
55
+ # # tests/fixtures/active_storage/blobs.yml
53
56
  # second_thumbnail_blob: <%= ActiveStorage::FixtureSet.blob(
54
57
  # filename: "second.svg",
55
58
  # ) %>
@@ -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 <tt>Gem::Version</tt>.
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 = 0
12
- TINY = 4
13
- PRE = nil
11
+ MINOR = 1
12
+ TINY = 3
13
+ PRE = "4"
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
- variants[name] = transformations
7
+ named_variants[name] = NamedVariant.new(transformations)
8
8
  end
9
9
 
10
- def variants
11
- @variants ||= {}
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
@@ -5,6 +5,8 @@ require "google/apis/iamcredentials_v1"
5
5
  require "google/cloud/storage"
6
6
 
7
7
  module ActiveStorage
8
+ # = Active Storage \GCS \Service
9
+ #
8
10
  # Wraps the Google Cloud Storage as an Active Storage service. See ActiveStorage::Service for the generic API
9
11
  # documentation that applies to all services.
10
12
  class Service::GCSService < Service
@@ -195,26 +197,15 @@ module ActiveStorage
195
197
  end
196
198
 
197
199
  def issuer
198
- @issuer ||= if @config[:gsa_email]
199
- @config[:gsa_email]
200
- else
201
- uri = URI.parse("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email")
202
- http = Net::HTTP.new(uri.host, uri.port)
203
- request = Net::HTTP::Get.new(uri.request_uri)
204
- request["Metadata-Flavor"] = "Google"
205
-
206
- begin
207
- response = http.request(request)
208
- rescue SocketError
209
- raise MetadataServerNotFoundError
210
- end
211
-
212
- if response.is_a?(Net::HTTPSuccess)
213
- response.body
214
- else
215
- raise MetadataServerError
216
- end
217
- end
200
+ @issuer ||= @config[:gsa_email].presence || email_from_metadata_server
201
+ end
202
+
203
+ def email_from_metadata_server
204
+ env = Google::Cloud.env
205
+ raise MetadataServerNotFoundError if !env.metadata?
206
+
207
+ email = env.lookup_metadata("instance", "service-accounts/default/email")
208
+ email.presence or raise MetadataServerError
218
209
  end
219
210
 
220
211
  def signer
@@ -3,6 +3,8 @@
3
3
  require "active_support/core_ext/module/delegation"
4
4
 
5
5
  module ActiveStorage
6
+ # = Active Storage Mirror \Service
7
+ #
6
8
  # Wraps a set of mirror services and provides a single ActiveStorage::Service object that will all
7
9
  # have the files uploaded to them. A +primary+ service is designated to answer calls to:
8
10
  # * +download+
@@ -30,13 +32,13 @@ module ActiveStorage
30
32
  @primary, @mirrors = primary, mirrors
31
33
  end
32
34
 
33
- # Upload the +io+ to the +key+ specified to all services. If a +checksum+ is provided, all services will
35
+ # Upload the +io+ to the +key+ specified to all services. The upload to the primary service is done synchronously
36
+ # whereas the upload to the mirrors is done asynchronously. If a +checksum+ is provided, all services will
34
37
  # ensure a match when the upload has completed or raise an ActiveStorage::IntegrityError.
35
38
  def upload(key, io, checksum: nil, **options)
36
- each_service.collect do |service|
37
- io.rewind
38
- service.upload key, io, checksum: checksum, **options
39
- end
39
+ io.rewind
40
+ primary.upload key, io, checksum: checksum, **options
41
+ mirror_later key, checksum: checksum
40
42
  end
41
43
 
42
44
  # Delete the file at the +key+ on all services.
@@ -49,6 +51,9 @@ module ActiveStorage
49
51
  perform_across_services :delete_prefixed, prefix
50
52
  end
51
53
 
54
+ def mirror_later(key, checksum:) # :nodoc:
55
+ ActiveStorage::MirrorJob.perform_later key, checksum: checksum
56
+ end
52
57
 
53
58
  # Copy the file at the +key+ from the primary service to each of the mirrors where it doesn't already exist.
54
59
  def mirror(key, checksum:)
@@ -6,6 +6,8 @@ require "aws-sdk-s3"
6
6
  require "active_support/core_ext/numeric/bytes"
7
7
 
8
8
  module ActiveStorage
9
+ # = Active Storage \S3 \Service
10
+ #
9
11
  # Wraps the Amazon Simple Storage Service (S3) as an Active Storage service.
10
12
  # See ActiveStorage::Service for the generic API documentation that applies to all services.
11
13
  class Service::S3Service < Service
@@ -6,6 +6,8 @@ require "action_dispatch"
6
6
  require "action_dispatch/http/content_disposition"
7
7
 
8
8
  module ActiveStorage
9
+ # = Active Storage \Service
10
+ #
9
11
  # Abstract class serving as an interface for concrete services.
10
12
  #
11
13
  # The available services are:
@@ -16,7 +18,7 @@ module ActiveStorage
16
18
  # * +AzureStorage+, to manage attachments through Microsoft Azure Storage.
17
19
  # * +Mirror+, to be able to use several services to manage attachments.
18
20
  #
19
- # Inside a Rails application, you can set-up your services through the
21
+ # Inside a \Rails application, you can set-up your services through the
20
22
  # generated <tt>config/storage.yml</tt> file and reference one
21
23
  # of the aforementioned constant under the +service+ key. For example:
22
24
  #
@@ -31,7 +33,7 @@ module ActiveStorage
31
33
  #
32
34
  # config.active_storage.service = :local
33
35
  #
34
- # If you are using Active Storage outside of a Ruby on Rails application, you
36
+ # If you are using Active Storage outside of a Ruby on \Rails application, you
35
37
  # can configure the service to use like this:
36
38
  #
37
39
  # ActiveStorage::Blob.service = ActiveStorage::Service.configure(