activestorage 7.0.4 → 7.1.5.1

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +213 -202
  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/preview_image_job.rb +16 -0
  19. data/app/jobs/active_storage/purge_job.rb +1 -1
  20. data/app/jobs/active_storage/transform_job.rb +12 -0
  21. data/app/models/active_storage/attachment.rb +101 -16
  22. data/app/models/active_storage/blob/analyzable.rb +4 -3
  23. data/app/models/active_storage/blob/identifiable.rb +1 -0
  24. data/app/models/active_storage/blob/representable.rb +15 -3
  25. data/app/models/active_storage/blob/servable.rb +22 -0
  26. data/app/models/active_storage/blob.rb +32 -68
  27. data/app/models/active_storage/current.rb +0 -10
  28. data/app/models/active_storage/filename.rb +2 -0
  29. data/app/models/active_storage/named_variant.rb +21 -0
  30. data/app/models/active_storage/preview.rb +11 -4
  31. data/app/models/active_storage/variant.rb +10 -7
  32. data/app/models/active_storage/variant_record.rb +0 -2
  33. data/app/models/active_storage/variant_with_record.rb +21 -7
  34. data/app/models/active_storage/variation.rb +5 -3
  35. data/config/routes.rb +6 -4
  36. data/db/migrate/20170806125915_create_active_storage_tables.rb +1 -1
  37. data/lib/active_storage/analyzer/audio_analyzer.rb +16 -4
  38. data/lib/active_storage/analyzer/image_analyzer.rb +2 -0
  39. data/lib/active_storage/analyzer/video_analyzer.rb +14 -5
  40. data/lib/active_storage/analyzer.rb +2 -0
  41. data/lib/active_storage/attached/changes/create_many.rb +8 -3
  42. data/lib/active_storage/attached/changes/create_one.rb +45 -3
  43. data/lib/active_storage/attached/many.rb +5 -4
  44. data/lib/active_storage/attached/model.rb +80 -47
  45. data/lib/active_storage/attached/one.rb +5 -4
  46. data/lib/active_storage/attached.rb +2 -0
  47. data/lib/active_storage/deprecator.rb +7 -0
  48. data/lib/active_storage/engine.rb +11 -7
  49. data/lib/active_storage/fixture_set.rb +12 -9
  50. data/lib/active_storage/gem_version.rb +4 -4
  51. data/lib/active_storage/log_subscriber.rb +12 -0
  52. data/lib/active_storage/previewer.rb +8 -1
  53. data/lib/active_storage/reflection.rb +3 -3
  54. data/lib/active_storage/service/azure_storage_service.rb +2 -0
  55. data/lib/active_storage/service/disk_service.rb +2 -0
  56. data/lib/active_storage/service/gcs_service.rb +11 -20
  57. data/lib/active_storage/service/mirror_service.rb +10 -5
  58. data/lib/active_storage/service/s3_service.rb +2 -0
  59. data/lib/active_storage/service.rb +4 -2
  60. data/lib/active_storage/transformers/transformer.rb +2 -0
  61. data/lib/active_storage/version.rb +1 -1
  62. data/lib/active_storage.rb +19 -3
  63. metadata +23 -31
@@ -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
  #
@@ -30,8 +74,10 @@ module ActiveStorage
30
74
  # The system has been designed to having you go through the ActiveStorage::Attached::One
31
75
  # proxy that provides the dynamic proxy to the associations and factory methods, like +attach+.
32
76
  #
33
- # If the +:dependent+ option isn't set, the attachment will be purged
34
- # (i.e. destroyed) whenever the record is destroyed.
77
+ # The +:dependent+ option defaults to +:purge_later+. This means the attachment will be
78
+ # purged (i.e. destroyed) in the background whenever the record is destroyed.
79
+ # If an ActiveJob::Backend queue adapter is not set in the application set it to
80
+ # +purge+ instead.
35
81
  #
36
82
  # If you need the attachment to use a service which differs from the globally configured one,
37
83
  # pass the +:service+ option. For instance:
@@ -59,7 +105,7 @@ module ActiveStorage
59
105
 
60
106
  def #{name}=(attachable)
61
107
  attachment_changes["#{name}"] =
62
- if attachable.nil?
108
+ if attachable.nil? || attachable == ""
63
109
  ActiveStorage::Attached::Changes::DeleteOne.new("#{name}", self)
64
110
  else
65
111
  ActiveStorage::Attached::Changes::CreateOne.new("#{name}", self, attachable)
@@ -70,7 +116,16 @@ module ActiveStorage
70
116
  has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record, inverse_of: :record, dependent: :destroy, strict_loading: strict_loading
71
117
  has_one :"#{name}_blob", through: :"#{name}_attachment", class_name: "ActiveStorage::Blob", source: :blob, strict_loading: strict_loading
72
118
 
73
- scope :"with_attached_#{name}", -> { includes("#{name}_attachment": :blob) }
119
+ scope :"with_attached_#{name}", -> {
120
+ if ActiveStorage.track_variants
121
+ includes("#{name}_attachment": { blob: {
122
+ variant_records: { image_attachment: :blob },
123
+ preview_image_attachment: { blob: { variant_records: { image_attachment: :blob } } }
124
+ } })
125
+ else
126
+ includes("#{name}_attachment": :blob)
127
+ end
128
+ }
74
129
 
75
130
  after_save { attachment_changes[name.to_s]&.save }
76
131
 
@@ -109,8 +164,10 @@ module ActiveStorage
109
164
  # The system has been designed to having you go through the ActiveStorage::Attached::Many
110
165
  # proxy that provides the dynamic proxy to the associations and factory methods, like +#attach+.
111
166
  #
112
- # If the +:dependent+ option isn't set, all the attachments will be purged
113
- # (i.e. destroyed) whenever the record is destroyed.
167
+ # The +:dependent+ option defaults to +:purge_later+. This means the attachments will be
168
+ # purged (i.e. destroyed) in the background whenever the record is destroyed.
169
+ # If an ActiveJob::Backend queue adapter is not set in the application set it to
170
+ # +purge+ instead.
114
171
  #
115
172
  # If you need the attachment to use a service which differs from the globally configured one,
116
173
  # pass the +:service+ option. For instance:
@@ -138,57 +195,25 @@ module ActiveStorage
138
195
 
139
196
  def #{name}=(attachables)
140
197
  attachables = Array(attachables).compact_blank
198
+ pending_uploads = attachment_changes["#{name}"].try(:pending_uploads)
141
199
 
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
200
+ attachment_changes["#{name}"] = if attachables.none?
201
+ ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
149
202
  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
203
+ ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables, pending_uploads: pending_uploads)
160
204
  end
161
205
  end
162
206
  CODE
163
207
 
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
208
+ has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record, dependent: :destroy, strict_loading: strict_loading
187
209
  has_many :"#{name}_blobs", through: :"#{name}_attachments", class_name: "ActiveStorage::Blob", source: :blob, strict_loading: strict_loading
188
210
 
189
211
  scope :"with_attached_#{name}", -> {
190
212
  if ActiveStorage.track_variants
191
- includes("#{name}_attachments": { blob: :variant_records })
213
+ includes("#{name}_attachments": { blob: {
214
+ variant_records: { image_attachment: :blob },
215
+ preview_image_attachment: { blob: { variant_records: { image_attachment: :blob } } }
216
+ } })
192
217
  else
193
218
  includes("#{name}_attachments": :blob)
194
219
  end
@@ -215,6 +240,14 @@ module ActiveStorage
215
240
  ActiveStorage::Blob.services.fetch(service) do
216
241
  raise ArgumentError, "Cannot configure service :#{service} for #{name}##{association_name}"
217
242
  end
243
+ else
244
+ validate_global_service_configuration
245
+ end
246
+ end
247
+
248
+ def validate_global_service_configuration
249
+ if connected? && ActiveStorage::Blob.table_exists? && Rails.configuration.active_storage.service.nil?
250
+ raise RuntimeError, "Missing Active Storage service name. Specify Active Storage service name for config.active_storage.service in config/environments/#{Rails.env}.rb"
218
251
  end
219
252
  end
220
253
  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 = 5
13
+ PRE = "1"
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