activestorage 7.0.0 → 7.1.0

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +158 -178
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +7 -7
  5. data/app/assets/javascripts/activestorage.esm.js +10 -18
  6. data/app/assets/javascripts/activestorage.js +11 -17
  7. data/app/controllers/active_storage/base_controller.rb +1 -1
  8. data/app/controllers/active_storage/blobs/proxy_controller.rb +2 -0
  9. data/app/controllers/active_storage/direct_uploads_controller.rb +1 -7
  10. data/app/controllers/active_storage/disk_controller.rb +4 -2
  11. data/app/controllers/active_storage/representations/proxy_controller.rb +3 -0
  12. data/app/controllers/concerns/active_storage/disable_session.rb +12 -0
  13. data/app/controllers/concerns/active_storage/file_server.rb +4 -1
  14. data/app/controllers/concerns/active_storage/streaming.rb +1 -0
  15. data/app/javascript/activestorage/blob_record.js +6 -10
  16. data/app/javascript/activestorage/direct_upload.js +3 -4
  17. data/app/javascript/activestorage/direct_upload_controller.js +1 -9
  18. data/app/javascript/activestorage/index.js +3 -1
  19. data/app/jobs/active_storage/analyze_job.rb +1 -1
  20. data/app/jobs/active_storage/mirror_job.rb +1 -1
  21. data/app/jobs/active_storage/purge_job.rb +1 -1
  22. data/app/jobs/active_storage/transform_job.rb +12 -0
  23. data/app/models/active_storage/attachment.rb +88 -14
  24. data/app/models/active_storage/blob/analyzable.rb +4 -3
  25. data/app/models/active_storage/blob/identifiable.rb +1 -0
  26. data/app/models/active_storage/blob/representable.rb +7 -3
  27. data/app/models/active_storage/blob.rb +27 -47
  28. data/app/models/active_storage/current.rb +0 -10
  29. data/app/models/active_storage/filename.rb +2 -0
  30. data/app/models/active_storage/named_variant.rb +21 -0
  31. data/app/models/active_storage/preview.rb +5 -3
  32. data/app/models/active_storage/variant.rb +11 -10
  33. data/app/models/active_storage/variant_with_record.rb +20 -8
  34. data/app/models/active_storage/variation.rb +6 -4
  35. data/config/routes.rb +6 -4
  36. data/db/migrate/20170806125915_create_active_storage_tables.rb +1 -1
  37. data/db/update_migrate/20190112182829_add_service_name_to_active_storage_blobs.rb +4 -0
  38. data/db/update_migrate/20191206030411_create_active_storage_variant_records.rb +2 -0
  39. data/db/update_migrate/20211119233751_remove_not_null_on_active_storage_blobs_checksum.rb +2 -0
  40. data/lib/active_storage/analyzer/audio_analyzer.rb +17 -5
  41. data/lib/active_storage/analyzer/image_analyzer/image_magick.rb +9 -7
  42. data/lib/active_storage/analyzer/image_analyzer/vips.rb +9 -7
  43. data/lib/active_storage/analyzer/image_analyzer.rb +2 -0
  44. data/lib/active_storage/analyzer/video_analyzer.rb +15 -6
  45. data/lib/active_storage/analyzer.rb +2 -0
  46. data/lib/active_storage/attached/changes/create_many.rb +8 -3
  47. data/lib/active_storage/attached/changes/create_one.rb +45 -3
  48. data/lib/active_storage/attached/many.rb +5 -4
  49. data/lib/active_storage/attached/model.rb +66 -43
  50. data/lib/active_storage/attached/one.rb +5 -4
  51. data/lib/active_storage/attached.rb +2 -0
  52. data/lib/active_storage/deprecator.rb +7 -0
  53. data/lib/active_storage/engine.rb +31 -9
  54. data/lib/active_storage/errors.rb +0 -3
  55. data/lib/active_storage/fixture_set.rb +7 -8
  56. data/lib/active_storage/gem_version.rb +2 -2
  57. data/lib/active_storage/log_subscriber.rb +12 -0
  58. data/lib/active_storage/previewer/video_previewer.rb +2 -0
  59. data/lib/active_storage/previewer.rb +8 -1
  60. data/lib/active_storage/reflection.rb +3 -3
  61. data/lib/active_storage/service/azure_storage_service.rb +2 -0
  62. data/lib/active_storage/service/disk_service.rb +2 -0
  63. data/lib/active_storage/service/gcs_service.rb +11 -20
  64. data/lib/active_storage/service/mirror_service.rb +10 -5
  65. data/lib/active_storage/service/s3_service.rb +2 -0
  66. data/lib/active_storage/service.rb +4 -2
  67. data/lib/active_storage/transformers/image_processing_transformer.rb +65 -0
  68. data/lib/active_storage/transformers/transformer.rb +2 -0
  69. data/lib/active_storage/version.rb +1 -1
  70. data/lib/active_storage.rb +310 -4
  71. metadata +21 -32
  72. data/lib/active_storage/direct_upload_token.rb +0 -59
@@ -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
- subchanges.each(&:upload)
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,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,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}", -> { includes("#{name}_attachment": :blob) }
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 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
193
+ attachment_changes["#{name}"] = if attachables.none?
194
+ ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
149
195
  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
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 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
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: :variant_records })
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.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
@@ -30,13 +30,12 @@ module ActiveStorage
30
30
  config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer::Vips, ActiveStorage::Analyzer::ImageAnalyzer::ImageMagick, ActiveStorage::Analyzer::VideoAnalyzer, ActiveStorage::Analyzer::AudioAnalyzer ]
31
31
  config.active_storage.paths = ActiveSupport::OrderedOptions.new
32
32
  config.active_storage.queues = ActiveSupport::InheritableOptions.new
33
+ config.active_storage.precompile_assets = true
33
34
 
34
35
  config.active_storage.variable_content_types = %w(
35
36
  image/png
36
37
  image/gif
37
- image/jpg
38
38
  image/jpeg
39
- image/pjpeg
40
39
  image/tiff
41
40
  image/bmp
42
41
  image/vnd.adobe.photoshop
@@ -50,13 +49,11 @@ module ActiveStorage
50
49
  config.active_storage.web_image_content_types = %w(
51
50
  image/png
52
51
  image/jpeg
53
- image/jpg
54
52
  image/gif
55
53
  )
56
54
 
57
55
  config.active_storage.content_types_to_serve_as_binary = %w(
58
56
  text/html
59
- text/javascript
60
57
  image/svg+xml
61
58
  application/postscript
62
59
  application/x-shockwave-flash
@@ -70,7 +67,6 @@ module ActiveStorage
70
67
  config.active_storage.content_types_allowed_inline = %w(
71
68
  image/png
72
69
  image/gif
73
- image/jpg
74
70
  image/jpeg
75
71
  image/tiff
76
72
  image/bmp
@@ -81,6 +77,10 @@ module ActiveStorage
81
77
 
82
78
  config.eager_load_namespaces << ActiveStorage
83
79
 
80
+ initializer "active_storage.deprecator", before: :load_environment_config do |app|
81
+ app.deprecators[:active_storage] = ActiveStorage.deprecator
82
+ end
83
+
84
84
  initializer "active_storage.configs" do
85
85
  config.after_initialize do |app|
86
86
  ActiveStorage.logger = app.config.active_storage.logger || Rails.logger
@@ -92,6 +92,21 @@ module ActiveStorage
92
92
  ActiveStorage.draw_routes = app.config.active_storage.draw_routes != false
93
93
  ActiveStorage.resolve_model_to_route = app.config.active_storage.resolve_model_to_route || :rails_storage_redirect
94
94
 
95
+ ActiveStorage.supported_image_processing_methods += app.config.active_storage.supported_image_processing_methods || []
96
+ ActiveStorage.unsupported_image_processing_arguments = app.config.active_storage.unsupported_image_processing_arguments || %w(
97
+ -debug
98
+ -display
99
+ -distribute-cache
100
+ -help
101
+ -path
102
+ -print
103
+ -set
104
+ -verbose
105
+ -version
106
+ -write
107
+ -write-mask
108
+ )
109
+
95
110
  ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || []
96
111
  ActiveStorage.web_image_content_types = app.config.active_storage.web_image_content_types || []
97
112
  ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || []
@@ -101,9 +116,14 @@ module ActiveStorage
101
116
  ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream"
102
117
  ActiveStorage.video_preview_arguments = app.config.active_storage.video_preview_arguments || "-y -vframes 1 -f image2"
103
118
 
104
- 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
105
126
 
106
- ActiveStorage.replace_on_assign_to_many = app.config.active_storage.replace_on_assign_to_many || false
107
127
  ActiveStorage.track_variants = app.config.active_storage.track_variants || false
108
128
  end
109
129
  end
@@ -167,8 +187,10 @@ module ActiveStorage
167
187
  end
168
188
 
169
189
  initializer "active_storage.asset" do
170
- if Rails.application.config.respond_to?(:assets)
171
- Rails.application.config.assets.precompile += %w( activestorage activestorage.esm )
190
+ config.after_initialize do |app|
191
+ if app.config.respond_to?(:assets) && app.config.active_storage.precompile_assets
192
+ app.config.assets.precompile += %w( activestorage activestorage.esm )
193
+ end
172
194
  end
173
195
  end
174
196
 
@@ -26,7 +26,4 @@ module ActiveStorage
26
26
 
27
27
  # Raised when a Previewer is unable to generate a preview image.
28
28
  class PreviewError < Error; end
29
-
30
- # Raised when direct upload fails because of the invalid token
31
- class InvalidDirectUploadTokenError < Error; end
32
29
  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
@@ -44,7 +43,7 @@ module ActiveStorage
44
43
 
45
44
  # Generate a YAML-encoded representation of an ActiveStorage::Blob
46
45
  # instance's attributes, resolve the file relative to the directory mentioned
47
- # by <tt>ActiveSupport::Testing::FileFixtures.file_fixture</tt>, and upload
46
+ # by ActiveSupport::Testing::FileFixtures.file_fixture, and upload
48
47
  # the file to the Service
49
48
  #
50
49
  # === Examples
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorage
4
- # Returns the version of the currently loaded 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
11
+ MINOR = 1
12
12
  TINY = 0
13
13
  PRE = nil
14
14
 
@@ -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,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "shellwords"
4
+
3
5
  module ActiveStorage
4
6
  class Previewer::VideoPreviewer < Previewer
5
7
  class << self
@@ -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