activestorage 6.0.0.beta1 → 6.0.1.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activestorage might be problematic. Click here for more details.

Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -0
  3. data/README.md +5 -3
  4. data/app/controllers/active_storage/disk_controller.rb +1 -1
  5. data/app/jobs/active_storage/analyze_job.rb +1 -0
  6. data/app/models/active_storage/attachment.rb +2 -0
  7. data/app/models/active_storage/blob.rb +27 -15
  8. data/app/models/active_storage/blob/representable.rb +5 -5
  9. data/app/models/active_storage/preview.rb +1 -1
  10. data/app/models/active_storage/variant.rb +4 -4
  11. data/app/models/active_storage/variation.rb +3 -3
  12. data/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb +3 -1
  13. data/lib/active_storage.rb +14 -5
  14. data/lib/active_storage/analyzer.rb +2 -2
  15. data/lib/active_storage/analyzer/image_analyzer.rb +11 -4
  16. data/lib/active_storage/analyzer/video_analyzer.rb +1 -1
  17. data/lib/active_storage/attached/changes/create_one.rb +1 -0
  18. data/lib/active_storage/attached/changes/delete_many.rb +4 -0
  19. data/lib/active_storage/attached/many.rb +1 -1
  20. data/lib/active_storage/attached/model.rb +12 -5
  21. data/lib/active_storage/downloader.rb +14 -15
  22. data/lib/active_storage/engine.rb +8 -0
  23. data/lib/active_storage/gem_version.rb +2 -2
  24. data/lib/active_storage/previewer.rb +4 -4
  25. data/lib/active_storage/previewer/poppler_pdf_previewer.rb +1 -1
  26. data/lib/active_storage/service.rb +4 -0
  27. data/lib/active_storage/service/azure_storage_service.rb +2 -2
  28. data/lib/active_storage/service/disk_service.rb +5 -1
  29. data/lib/active_storage/service/gcs_service.rb +1 -1
  30. data/lib/active_storage/service/mirror_service.rb +1 -1
  31. data/lib/active_storage/service/s3_service.rb +2 -2
  32. data/lib/active_storage/transformers/image_processing_transformer.rb +1 -1
  33. metadata +27 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c76cc0190a59a7e19312ae68c2b6c3712f62eca50d9fa4d287cdc6feca94a5aa
4
- data.tar.gz: 6d8f47c3d81c83b98b0dfc254a6e9fe6ae9b447501db6176cb96691ceb77c61c
3
+ metadata.gz: 6d424efd1fa5c4046399c3274aca014257a2e72230feaf69e3aa7217ad65fc21
4
+ data.tar.gz: a1d549b32cd85fe13d6577ade41f2269588ab483b4a3e6c4f376a6c3ff9feeeb
5
5
  SHA512:
6
- metadata.gz: a86e9a4fd481f1aa56946f24ff7e02869fe6591952929021e5c46428b230a34f18f324847db51eb0ebfd17ed8f43fb61f215798997766d072a6dbf51de21369d
7
- data.tar.gz: 0231fa7771d92de8b9a9012291d69e2c5b976af3fa3f932c5fd0331f9cccd76ad90887d1101f96d47a6c7949832dabff1f1011412ab27a4a4ed76091ba851e10
6
+ metadata.gz: 84ed4b45a68bdf07bbe8b994d07338eecfe552a5fe37c98913dbcd0e5f3aa67328571de163cceac14c836533ffd1d3e06af6177614777415f110f05eab2b305c
7
+ data.tar.gz: 9c5824e5f4a5084ef5635abd5f15ecc6182f6a713cafcc4959aa6873d0c701722df149278f6bc27c7d9ac7ef2dca122595830ecb4e98d7199b7f5629c5e71f7a
@@ -1,5 +1,57 @@
1
+ ## Rails 6.0.1.rc1 (October 31, 2019) ##
2
+
3
+ * `ActiveStorage::AnalyzeJob`s are discarded on `ActiveRecord::RecordNotFound` errors.
4
+
5
+ *George Claghorn*
6
+
7
+ * Blobs are recorded in the database before being uploaded to the service.
8
+ This fixes that generated blob keys could silently collide, leading to
9
+ data loss.
10
+
11
+ *Julik Tarkhanov*
12
+
13
+
14
+ ## Rails 6.0.0 (August 16, 2019) ##
15
+
16
+ * No changes.
17
+
18
+
19
+ ## Rails 6.0.0.rc2 (July 22, 2019) ##
20
+
21
+ * No changes.
22
+
23
+
24
+ ## Rails 6.0.0.rc1 (April 24, 2019) ##
25
+
26
+ * Don't raise when analyzing an image whose type is unsupported by ImageMagick.
27
+
28
+ Fixes #36065.
29
+
30
+ *Guilherme Mansur*
31
+
32
+ * Permit generating variants of BMP images.
33
+
34
+ *Younes Serraj*
35
+
36
+
37
+ ## Rails 6.0.0.beta3 (March 11, 2019) ##
38
+
39
+ * No changes.
40
+
41
+
42
+ ## Rails 6.0.0.beta2 (February 25, 2019) ##
43
+
44
+ * No changes.
45
+
46
+
1
47
  ## Rails 6.0.0.beta1 (January 18, 2019) ##
2
48
 
49
+ * [Rename npm package](https://github.com/rails/rails/pull/34905) from
50
+ [`activestorage`](https://www.npmjs.com/package/activestorage) to
51
+ [`@rails/activestorage`](https://www.npmjs.com/package/@rails/activestorage).
52
+
53
+ *Javan Makhmali*
54
+
3
55
  * Replace `config.active_storage.queue` with two options that indicate which
4
56
  queues analysis and purge jobs should use, respectively:
5
57
 
data/README.md CHANGED
@@ -4,7 +4,9 @@ Active Storage makes it simple to upload and reference files in cloud services l
4
4
 
5
5
  Files can be uploaded from the server to the cloud or directly from the client to the cloud.
6
6
 
7
- Image files can furthermore be transformed using on-demand variants for quality, aspect ratio, size, or any other [MiniMagick](https://github.com/minimagick/minimagick) or [Vips](http://www.rubydoc.info/gems/ruby-vips/Vips/Image) supported transformation.
7
+ Image files can furthermore be transformed using on-demand variants for quality, aspect ratio, size, or any other [MiniMagick](https://github.com/minimagick/minimagick) or [Vips](https://www.rubydoc.info/gems/ruby-vips/Vips/Image) supported transformation.
8
+
9
+ You can read more about Active Storage in the [Active Storage Overview](https://edgeguides.rubyonrails.org/active_storage_overview.html) guide.
8
10
 
9
11
  ## Compared to other storage solutions
10
12
 
@@ -101,7 +103,7 @@ Variation of image attachment:
101
103
 
102
104
  ```erb
103
105
  <%# Hitting the variant URL will lazy transform the original blob and then redirect to its new service location %>
104
- <%= image_tag user.avatar.variant(resize_to_fit: [100, 100]) %>
106
+ <%= image_tag user.avatar.variant(resize_to_limit: [100, 100]) %>
105
107
  ```
106
108
 
107
109
  ## Direct uploads
@@ -149,7 +151,7 @@ Active Storage is released under the [MIT License](https://opensource.org/licens
149
151
 
150
152
  API documentation is at:
151
153
 
152
- * http://api.rubyonrails.org
154
+ * https://api.rubyonrails.org
153
155
 
154
156
  Bug reports for the Ruby on Rails project can be filed here:
155
157
 
@@ -3,7 +3,7 @@
3
3
  # Serves files stored with the disk service in the same way that the cloud services do.
4
4
  # This means using expiring, signed URLs that are meant for immediate access, not permanent linking.
5
5
  # Always go through the BlobsController, or your own authenticated controller, rather than directly
6
- # to the service url.
6
+ # to the service URL.
7
7
  class ActiveStorage::DiskController < ActiveStorage::BaseController
8
8
  skip_forgery_protection
9
9
 
@@ -4,6 +4,7 @@
4
4
  class ActiveStorage::AnalyzeJob < ActiveStorage::BaseJob
5
5
  queue_as { ActiveStorage.queues[:analysis] }
6
6
 
7
+ discard_on ActiveRecord::RecordNotFound
7
8
  retry_on ActiveStorage::IntegrityError, attempts: 10, wait: :exponentially_longer
8
9
 
9
10
  def perform(blob)
@@ -46,3 +46,5 @@ class ActiveStorage::Attachment < ActiveRecord::Base
46
46
  record.attachment_reflections[name]&.options[:dependent]
47
47
  end
48
48
  end
49
+
50
+ ActiveSupport.run_load_hooks :active_storage_attachment, ActiveStorage::Attachment
@@ -5,8 +5,9 @@ require "active_storage/downloader"
5
5
  # A blob is a record that contains the metadata about a file and a key for where that file resides on the service.
6
6
  # Blobs can be created in two ways:
7
7
  #
8
- # 1. Subsequent to the file being uploaded server-side to the service via <tt>create_after_upload!</tt>.
9
- # 2. Ahead of the file being directly uploaded client-side to the service via <tt>create_before_direct_upload!</tt>.
8
+ # 1. Ahead of the file being uploaded server-side to the service, via <tt>create_and_upload!</tt>. A rewindable
9
+ # <tt>io</tt> with the file contents must be available at the server for this operation.
10
+ # 2. Ahead of the file being directly uploaded client-side to the service, via <tt>create_before_direct_upload!</tt>.
10
11
  #
11
12
  # The first option doesn't require any client-side JavaScript integration, and can be used by any other back-end
12
13
  # service that deals with files. The second option is faster, since you're not using your own server as a staging
@@ -63,14 +64,23 @@ class ActiveStorage::Blob < ActiveRecord::Base
63
64
  end
64
65
  end
65
66
 
66
- # Returns a saved blob instance after the +io+ has been uploaded to the service. Note, the blob is first built,
67
- # then the +io+ is uploaded, then the blob is saved. This is done this way to avoid uploading (which may take
68
- # time), while having an open database transaction.
67
+ def create_after_unfurling!(io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil) #:nodoc:
68
+ build_after_unfurling(io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap(&:save!)
69
+ end
70
+
71
+ # Creates a new blob instance and then uploads the contents of the given <tt>io</tt> to the
72
+ # service. The blob instance is saved before the upload begins to avoid clobbering another due
73
+ # to key collisions.
74
+ #
69
75
  # When providing a content type, pass <tt>identify: false</tt> to bypass automatic content type inference.
70
- def create_after_upload!(io:, filename:, content_type: nil, metadata: nil, identify: true)
71
- build_after_upload(io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap(&:save!)
76
+ def create_and_upload!(io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil)
77
+ create_after_unfurling!(io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap do |blob|
78
+ blob.upload_without_unfurling(io)
79
+ end
72
80
  end
73
81
 
82
+ alias_method :create_after_upload!, :create_and_upload!
83
+
74
84
  # Returns a saved blob _without_ uploading a file to the service. This blob will point to a key where there is
75
85
  # no file yet. It's intended to be used together with a client-side upload, which will first create the blob
76
86
  # in order to produce the signed URL for uploading. This signed URL points to the key generated by the blob.
@@ -165,8 +175,9 @@ class ActiveStorage::Blob < ActiveRecord::Base
165
175
  # and store that in +byte_size+ on the blob record. The content type is automatically extracted from the +io+ unless
166
176
  # you specify a +content_type+ and pass +identify+ as false.
167
177
  #
168
- # Normally, you do not have to call this method directly at all. Use the factory class methods of +build_after_upload+
169
- # and +create_after_upload!+.
178
+ # Normally, you do not have to call this method directly at all. Use the +create_and_upload!+ class method instead.
179
+ # If you do use this method directly, make sure you are using it on a persisted Blob as otherwise another blob's
180
+ # data might get overwritten on the service.
170
181
  def upload(io, identify: true)
171
182
  unfurl io, identify: identify
172
183
  upload_without_unfurling io
@@ -193,17 +204,18 @@ class ActiveStorage::Blob < ActiveRecord::Base
193
204
  #
194
205
  # The tempfile's name is prefixed with +ActiveStorage-+ and the blob's ID. Its extension matches that of the blob.
195
206
  #
196
- # By default, the tempfile is created in <tt>Dir.tmpdir</tt>. Pass +tempdir:+ to create it in a different directory:
207
+ # By default, the tempfile is created in <tt>Dir.tmpdir</tt>. Pass +tmpdir:+ to create it in a different directory:
197
208
  #
198
- # blob.open(tempdir: "/path/to/tmp") do |file|
209
+ # blob.open(tmpdir: "/path/to/tmp") do |file|
199
210
  # # ...
200
211
  # end
201
212
  #
202
213
  # The tempfile is automatically closed and unlinked after the given block is executed.
203
214
  #
204
215
  # Raises ActiveStorage::IntegrityError if the downloaded data does not match the blob's checksum.
205
- def open(tempdir: nil, &block)
206
- ActiveStorage::Downloader.new(self, tempdir: tempdir).download_blob_to_tempfile(&block)
216
+ def open(tmpdir: nil, &block)
217
+ service.open key, checksum: checksum,
218
+ name: [ "ActiveStorage-#{id}-", filename.extension_with_delimiter ], tmpdir: tmpdir, &block
207
219
  end
208
220
 
209
221
 
@@ -272,6 +284,6 @@ class ActiveStorage::Blob < ActiveRecord::Base
272
284
  { content_type: content_type }
273
285
  end
274
286
  end
275
-
276
- ActiveSupport.run_load_hooks(:active_storage_blob, self)
277
287
  end
288
+
289
+ ActiveSupport.run_load_hooks :active_storage_blob, ActiveStorage::Blob
@@ -10,7 +10,7 @@ module ActiveStorage::Blob::Representable
10
10
  # Returns an ActiveStorage::Variant instance with the set of +transformations+ provided. This is only relevant for image
11
11
  # files, and it allows any image to be transformed for size, colors, and the like. Example:
12
12
  #
13
- # avatar.variant(resize_to_fit: [100, 100]).processed.service_url
13
+ # avatar.variant(resize_to_limit: [100, 100]).processed.service_url
14
14
  #
15
15
  # This will create and process a variant of the avatar blob that's constrained to a height and width of 100px.
16
16
  # Then it'll upload said variant to the service according to a derivative key of the blob and the transformations.
@@ -18,7 +18,7 @@ module ActiveStorage::Blob::Representable
18
18
  # Frequently, though, you don't actually want to transform the variant right away. But rather simply refer to a
19
19
  # specific variant that can be created by a controller on-demand. Like so:
20
20
  #
21
- # <%= image_tag Current.user.avatar.variant(resize_to_fit: [100, 100]) %>
21
+ # <%= image_tag Current.user.avatar.variant(resize_to_limit: [100, 100]) %>
22
22
  #
23
23
  # This will create a URL for that specific blob with that specific variant, which the ActiveStorage::RepresentationsController
24
24
  # can then produce on-demand.
@@ -43,13 +43,13 @@ module ActiveStorage::Blob::Representable
43
43
  # from a non-image blob. Active Storage comes with built-in previewers for videos and PDF documents. The video previewer
44
44
  # extracts the first frame from a video and the PDF previewer extracts the first page from a PDF document.
45
45
  #
46
- # blob.preview(resize_to_fit: [100, 100]).processed.service_url
46
+ # blob.preview(resize_to_limit: [100, 100]).processed.service_url
47
47
  #
48
48
  # Avoid processing previews synchronously in views. Instead, link to a controller action that processes them on demand.
49
49
  # Active Storage provides one, but you may want to create your own (for example, if you need authentication). Here’s
50
50
  # how to use the built-in version:
51
51
  #
52
- # <%= image_tag video.preview(resize_to_fit: [100, 100]) %>
52
+ # <%= image_tag video.preview(resize_to_limit: [100, 100]) %>
53
53
  #
54
54
  # This method raises ActiveStorage::UnpreviewableError if no previewer accepts the receiving blob. To determine
55
55
  # whether a blob is accepted by any previewer, call ActiveStorage::Blob#previewable?.
@@ -69,7 +69,7 @@ module ActiveStorage::Blob::Representable
69
69
 
70
70
  # Returns an ActiveStorage::Preview for a previewable blob or an ActiveStorage::Variant for a variable image blob.
71
71
  #
72
- # blob.representation(resize_to_fit: [100, 100]).processed.service_url
72
+ # blob.representation(resize_to_limit: [100, 100]).processed.service_url
73
73
  #
74
74
  # Raises ActiveStorage::UnrepresentableError if the receiving blob is neither variable nor previewable. Call
75
75
  # ActiveStorage::Blob#representable? to determine whether a blob is representable.
@@ -38,7 +38,7 @@ class ActiveStorage::Preview
38
38
 
39
39
  # Processes the preview if it has not been processed yet. Returns the receiving Preview instance for convenience:
40
40
  #
41
- # blob.preview(resize_to_fit: [100, 100]).processed.service_url
41
+ # blob.preview(resize_to_limit: [100, 100]).processed.service_url
42
42
  #
43
43
  # Processing a preview generates an image from its blob and attaches the preview image to the blob. Because the preview
44
44
  # image is stored with the blob, it is only generated once.
@@ -27,7 +27,7 @@ require "ostruct"
27
27
  # To refer to such a delayed on-demand variant, simply link to the variant through the resolved route provided
28
28
  # by Active Storage like so:
29
29
  #
30
- # <%= image_tag Current.user.avatar.variant(resize_to_fit: [100, 100]) %>
30
+ # <%= image_tag Current.user.avatar.variant(resize_to_limit: [100, 100]) %>
31
31
  #
32
32
  # This will create a URL for that specific blob with that specific variant, which the ActiveStorage::RepresentationsController
33
33
  # can then produce on-demand.
@@ -36,15 +36,15 @@ require "ostruct"
36
36
  # has already been processed and uploaded to the service, and, if so, just return that. Otherwise it will perform
37
37
  # the transformations, upload the variant to the service, and return itself again. Example:
38
38
  #
39
- # avatar.variant(resize_to_fit: [100, 100]).processed.service_url
39
+ # avatar.variant(resize_to_limit: [100, 100]).processed.service_url
40
40
  #
41
41
  # This will create and process a variant of the avatar blob that's constrained to a height and width of 100.
42
42
  # Then it'll upload said variant to the service according to a derivative key of the blob and the transformations.
43
43
  #
44
44
  # You can combine any number of ImageMagick/libvips operations into a variant, as well as any macros provided by the
45
- # ImageProcessing gem (such as +resize_to_fit+):
45
+ # ImageProcessing gem (such as +resize_to_limit+):
46
46
  #
47
- # avatar.variant(resize_to_fit: [800, 800], monochrome: true, rotate: "-90")
47
+ # avatar.variant(resize_to_limit: [800, 800], monochrome: true, rotate: "-90")
48
48
  #
49
49
  # Visit the following links for a list of available ImageProcessing commands and ImageMagick/libvips operations:
50
50
  #
@@ -6,7 +6,7 @@
6
6
  # In case you do need to use this directly, it's instantiated using a hash of transformations where
7
7
  # the key is the command and the value is the arguments. Example:
8
8
  #
9
- # ActiveStorage::Variation.new(resize_to_fit: [100, 100], monochrome: true, trim: true, rotate: "-90")
9
+ # ActiveStorage::Variation.new(resize_to_limit: [100, 100], monochrome: true, trim: true, rotate: "-90")
10
10
  #
11
11
  # The options map directly to {ImageProcessing}[https://github.com/janko-m/image_processing] commands.
12
12
  class ActiveStorage::Variation
@@ -40,7 +40,7 @@ class ActiveStorage::Variation
40
40
  end
41
41
 
42
42
  def initialize(transformations)
43
- @transformations = transformations
43
+ @transformations = transformations.deep_symbolize_keys
44
44
  end
45
45
 
46
46
  # Accepts a File object, performs the +transformations+ against it, and
@@ -64,7 +64,7 @@ class ActiveStorage::Variation
64
64
  begin
65
65
  require "image_processing"
66
66
  rescue LoadError
67
- ActiveSupport::Deprecation.warn <<~WARNING
67
+ ActiveSupport::Deprecation.warn <<~WARNING.squish
68
68
  Generating image variants will require the image_processing gem in Rails 6.1.
69
69
  Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.
70
70
  WARNING
@@ -1,6 +1,8 @@
1
1
  class AddForeignKeyConstraintToActiveStorageAttachmentsForBlobId < ActiveRecord::Migration[6.0]
2
2
  def up
3
- unless foreign_key_exists?(:active_storage_attachments, column: :blob_id)
3
+ return if foreign_key_exists?(:active_storage_attachments, column: :blob_id)
4
+
5
+ if table_exists?(:active_storage_blobs)
4
6
  add_foreign_key :active_storage_attachments, :active_storage_blobs, column: :blob_id
5
7
  end
6
8
  end
@@ -26,6 +26,7 @@
26
26
  require "active_record"
27
27
  require "active_support"
28
28
  require "active_support/rails"
29
+ require "active_support/core_ext/numeric/time"
29
30
 
30
31
  require "active_storage/version"
31
32
  require "active_storage/errors"
@@ -42,18 +43,26 @@ module ActiveStorage
42
43
 
43
44
  mattr_accessor :logger
44
45
  mattr_accessor :verifier
46
+ mattr_accessor :variant_processor, default: :mini_magick
47
+
45
48
  mattr_accessor :queues, default: {}
49
+
46
50
  mattr_accessor :previewers, default: []
47
- mattr_accessor :analyzers, default: []
48
- mattr_accessor :variant_processor, default: :mini_magick
51
+ mattr_accessor :analyzers, default: []
52
+
49
53
  mattr_accessor :paths, default: {}
50
- mattr_accessor :variable_content_types, default: []
54
+
55
+ mattr_accessor :variable_content_types, default: []
56
+ mattr_accessor :binary_content_type, default: "application/octet-stream"
51
57
  mattr_accessor :content_types_to_serve_as_binary, default: []
52
- mattr_accessor :content_types_allowed_inline, default: []
53
- mattr_accessor :binary_content_type, default: "application/octet-stream"
58
+ mattr_accessor :content_types_allowed_inline, default: []
59
+
54
60
  mattr_accessor :service_urls_expire_in, default: 5.minutes
61
+
55
62
  mattr_accessor :routes_prefix, default: "/rails/active_storage"
56
63
 
64
+ mattr_accessor :replace_on_assign_to_many, default: false
65
+
57
66
  module Transformers
58
67
  extend ActiveSupport::Autoload
59
68
 
@@ -24,14 +24,14 @@ module ActiveStorage
24
24
  private
25
25
  # Downloads the blob to a tempfile on disk. Yields the tempfile.
26
26
  def download_blob_to_tempfile(&block) #:doc:
27
- blob.open tempdir: tempdir, &block
27
+ blob.open tmpdir: tmpdir, &block
28
28
  end
29
29
 
30
30
  def logger #:doc:
31
31
  ActiveStorage.logger
32
32
  end
33
33
 
34
- def tempdir #:doc:
34
+ def tmpdir #:doc:
35
35
  Dir.tmpdir
36
36
  end
37
37
  end
@@ -25,17 +25,24 @@ module ActiveStorage
25
25
  { width: image.width, height: image.height }
26
26
  end
27
27
  end
28
- rescue LoadError
29
- logger.info "Skipping image analysis because the mini_magick gem isn't installed"
30
- {}
31
28
  end
32
29
 
33
30
  private
34
31
  def read_image
35
32
  download_blob_to_tempfile do |file|
36
33
  require "mini_magick"
37
- yield MiniMagick::Image.new(file.path)
34
+ image = MiniMagick::Image.new(file.path)
35
+
36
+ if image.valid?
37
+ yield image
38
+ else
39
+ logger.info "Skipping image analysis because ImageMagick doesn't support the file"
40
+ {}
41
+ end
38
42
  end
43
+ rescue LoadError
44
+ logger.info "Skipping image analysis because the mini_magick gem isn't installed"
45
+ {}
39
46
  end
40
47
 
41
48
  def rotated_image?(image)
@@ -11,7 +11,7 @@ module ActiveStorage
11
11
  #
12
12
  # Example:
13
13
  #
14
- # ActiveStorage::VideoAnalyzer.new(blob).metadata
14
+ # ActiveStorage::Analyzer::VideoAnalyzer.new(blob).metadata
15
15
  # # => { width: 640.0, height: 480.0, duration: 5.0, angle: 0, display_aspect_ratio: [4, 3] }
16
16
  #
17
17
  # When a video's angle is 90 or 270 degrees, its width and height are automatically swapped for convenience.
@@ -30,6 +30,7 @@ module ActiveStorage
30
30
 
31
31
  def save
32
32
  record.public_send("#{name}_attachment=", attachment)
33
+ record.public_send("#{name}_blob=", blob)
33
34
  end
34
35
 
35
36
  private
@@ -8,6 +8,10 @@ module ActiveStorage
8
8
  @name, @record = name, record
9
9
  end
10
10
 
11
+ def attachables
12
+ []
13
+ end
14
+
11
15
  def attachments
12
16
  ActiveStorage::Attachment.none
13
17
  end
@@ -31,7 +31,7 @@ module ActiveStorage
31
31
  if record.persisted? && !record.changed?
32
32
  record.update(name => blobs + attachables.flatten)
33
33
  else
34
- record.public_send("#{name}=", blobs + attachables.flatten)
34
+ record.public_send("#{name}=", (change&.attachables || blobs) + attachables.flatten)
35
35
  end
36
36
  end
37
37
 
@@ -93,12 +93,19 @@ module ActiveStorage
93
93
  end
94
94
 
95
95
  def #{name}=(attachables)
96
- attachment_changes["#{name}"] =
97
- if attachables.nil? || Array(attachables).none?
98
- ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
99
- else
100
- ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables)
96
+ if ActiveStorage.replace_on_assign_to_many
97
+ attachment_changes["#{name}"] =
98
+ if Array(attachables).none?
99
+ ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
100
+ else
101
+ ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables)
102
+ end
103
+ else
104
+ if Array(attachables).any?
105
+ attachment_changes["#{name}"] =
106
+ ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, #{name}.blobs + attachables)
101
107
  end
108
+ end
102
109
  end
103
110
  CODE
104
111
 
@@ -2,24 +2,23 @@
2
2
 
3
3
  module ActiveStorage
4
4
  class Downloader #:nodoc:
5
- def initialize(blob, tempdir: nil)
6
- @blob = blob
7
- @tempdir = tempdir
5
+ attr_reader :service
6
+
7
+ def initialize(service)
8
+ @service = service
8
9
  end
9
10
 
10
- def download_blob_to_tempfile
11
- open_tempfile do |file|
12
- download_blob_to file
13
- verify_integrity_of file
11
+ def open(key, checksum:, name: "ActiveStorage-", tmpdir: nil)
12
+ open_tempfile(name, tmpdir) do |file|
13
+ download key, file
14
+ verify_integrity_of file, checksum: checksum
14
15
  yield file
15
16
  end
16
17
  end
17
18
 
18
19
  private
19
- attr_reader :blob, :tempdir
20
-
21
- def open_tempfile
22
- file = Tempfile.open([ "ActiveStorage-#{blob.id}-", blob.filename.extension_with_delimiter ], tempdir)
20
+ def open_tempfile(name, tmpdir = nil)
21
+ file = Tempfile.open(name, tmpdir)
23
22
 
24
23
  begin
25
24
  yield file
@@ -28,15 +27,15 @@ module ActiveStorage
28
27
  end
29
28
  end
30
29
 
31
- def download_blob_to(file)
30
+ def download(key, file)
32
31
  file.binmode
33
- blob.download { |chunk| file.write(chunk) }
32
+ service.download(key) { |chunk| file.write(chunk) }
34
33
  file.flush
35
34
  file.rewind
36
35
  end
37
36
 
38
- def verify_integrity_of(file)
39
- unless Digest::MD5.file(file).base64digest == blob.checksum
37
+ def verify_integrity_of(file, checksum:)
38
+ unless Digest::MD5.file(file).base64digest == checksum
40
39
  raise ActiveStorage::IntegrityError
41
40
  end
42
41
  end
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails"
4
+ require "action_controller/railtie"
5
+ require "active_job/railtie"
6
+ require "active_record/railtie"
7
+
4
8
  require "active_storage"
5
9
 
6
10
  require "active_storage/previewer/poppler_pdf_previewer"
@@ -29,6 +33,7 @@ module ActiveStorage
29
33
  image/jpeg
30
34
  image/pjpeg
31
35
  image/tiff
36
+ image/bmp
32
37
  image/vnd.adobe.photoshop
33
38
  image/vnd.microsoft.icon
34
39
  )
@@ -52,6 +57,7 @@ module ActiveStorage
52
57
  image/jpg
53
58
  image/jpeg
54
59
  image/tiff
60
+ image/bmp
55
61
  image/vnd.adobe.photoshop
56
62
  image/vnd.microsoft.icon
57
63
  application/pdf
@@ -73,6 +79,8 @@ module ActiveStorage
73
79
  ActiveStorage.service_urls_expire_in = app.config.active_storage.service_urls_expire_in || 5.minutes
74
80
  ActiveStorage.content_types_allowed_inline = app.config.active_storage.content_types_allowed_inline || []
75
81
  ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream"
82
+
83
+ ActiveStorage.replace_on_assign_to_many = app.config.active_storage.replace_on_assign_to_many || false
76
84
  end
77
85
  end
78
86
 
@@ -9,8 +9,8 @@ module ActiveStorage
9
9
  module VERSION
10
10
  MAJOR = 6
11
11
  MINOR = 0
12
- TINY = 0
13
- PRE = "beta1"
12
+ TINY = 1
13
+ PRE = "rc1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -26,7 +26,7 @@ module ActiveStorage
26
26
  private
27
27
  # Downloads the blob to a tempfile on disk. Yields the tempfile.
28
28
  def download_blob_to_tempfile(&block) #:doc:
29
- blob.open tempdir: tempdir, &block
29
+ blob.open tmpdir: tmpdir, &block
30
30
  end
31
31
 
32
32
  # Executes a system command, capturing its binary output in a tempfile. Yields the tempfile.
@@ -42,7 +42,7 @@ module ActiveStorage
42
42
  # end
43
43
  # end
44
44
  #
45
- # The output tempfile is opened in the directory returned by #tempdir.
45
+ # The output tempfile is opened in the directory returned by #tmpdir.
46
46
  def draw(*argv) #:doc:
47
47
  open_tempfile do |file|
48
48
  instrument :preview, key: blob.key do
@@ -54,7 +54,7 @@ module ActiveStorage
54
54
  end
55
55
 
56
56
  def open_tempfile
57
- tempfile = Tempfile.open("ActiveStorage-", tempdir)
57
+ tempfile = Tempfile.open("ActiveStorage-", tmpdir)
58
58
 
59
59
  begin
60
60
  yield tempfile
@@ -77,7 +77,7 @@ module ActiveStorage
77
77
  ActiveStorage.logger
78
78
  end
79
79
 
80
- def tempdir #:doc:
80
+ def tmpdir #:doc:
81
81
  Dir.tmpdir
82
82
  end
83
83
  end
@@ -28,7 +28,7 @@ module ActiveStorage
28
28
 
29
29
  private
30
30
  def draw_first_page_from(file, &block)
31
- # use 72 dpi to match thumbnail dimesions of the PDF
31
+ # use 72 dpi to match thumbnail dimensions of the PDF
32
32
  draw self.class.pdftoppm_path, "-singlefile", "-r", "72", "-png", file.path, &block
33
33
  end
34
34
  end
@@ -82,6 +82,10 @@ module ActiveStorage
82
82
  raise NotImplementedError
83
83
  end
84
84
 
85
+ def open(*args, &block)
86
+ ActiveStorage::Downloader.new(self).open(*args, &block)
87
+ end
88
+
85
89
  # Delete the file at the +key+.
86
90
  def delete(key)
87
91
  raise NotImplementedError
@@ -10,8 +10,8 @@ module ActiveStorage
10
10
  class Service::AzureStorageService < Service
11
11
  attr_reader :client, :blobs, :container, :signer
12
12
 
13
- def initialize(storage_account_name:, storage_access_key:, container:)
14
- @client = Azure::Storage::Client.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key)
13
+ def initialize(storage_account_name:, storage_access_key:, container:, **options)
14
+ @client = Azure::Storage::Client.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key, **options)
15
15
  @signer = Azure::Storage::Core::Auth::SharedAccessSignature.new(storage_account_name, storage_access_key)
16
16
  @blobs = client.blob_client
17
17
  @container = container
@@ -84,8 +84,12 @@ module ActiveStorage
84
84
  purpose: :blob_key }
85
85
  )
86
86
 
87
+ current_uri = URI.parse(current_host)
88
+
87
89
  generated_url = url_helpers.rails_disk_service_url(verified_key_with_expiration,
88
- host: current_host,
90
+ protocol: current_uri.scheme,
91
+ host: current_uri.host,
92
+ port: current_uri.port,
89
93
  disposition: content_disposition,
90
94
  content_type: content_type,
91
95
  filename: filename
@@ -131,7 +131,7 @@ module ActiveStorage
131
131
  end
132
132
 
133
133
  def bucket
134
- @bucket ||= client.bucket(config.fetch(:bucket))
134
+ @bucket ||= client.bucket(config.fetch(:bucket), skip_lookup: true)
135
135
  end
136
136
 
137
137
  def client
@@ -9,7 +9,7 @@ module ActiveStorage
9
9
  class Service::MirrorService < Service
10
10
  attr_reader :primary, :mirrors
11
11
 
12
- delegate :download, :download_chunk, :exist?, :url, to: :primary
12
+ delegate :download, :download_chunk, :exist?, :url, :path_for, to: :primary
13
13
 
14
14
  # Stitch together from named services.
15
15
  def self.build(primary:, mirrors:, configurator:, **options) #:nodoc:
@@ -16,9 +16,9 @@ module ActiveStorage
16
16
  @upload_options = upload
17
17
  end
18
18
 
19
- def upload(key, io, checksum: nil, **)
19
+ def upload(key, io, checksum: nil, content_type: nil, **)
20
20
  instrument :upload, key: key, checksum: checksum do
21
- object_for(key).put(upload_options.merge(body: io, content_md5: checksum))
21
+ object_for(key).put(upload_options.merge(body: io, content_md5: checksum, content_type: content_type))
22
22
  rescue Aws::S3::Errors::BadDigest
23
23
  raise ActiveStorage::IntegrityError
24
24
  end
@@ -22,7 +22,7 @@ module ActiveStorage
22
22
  def operations
23
23
  transformations.each_with_object([]) do |(name, argument), list|
24
24
  if name.to_s == "combine_options"
25
- ActiveSupport::Deprecation.warn <<~WARNING
25
+ ActiveSupport::Deprecation.warn <<~WARNING.squish
26
26
  Active Storage's ImageProcessing transformer doesn't support :combine_options,
27
27
  as it always generates a single ImageMagick command. Passing :combine_options will
28
28
  not be supported in Rails 6.1.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activestorage
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.beta1
4
+ version: 6.0.1.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-18 00:00:00.000000000 Z
11
+ date: 2019-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -16,28 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 6.0.0.beta1
19
+ version: 6.0.1.rc1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 6.0.0.beta1
26
+ version: 6.0.1.rc1
27
+ - !ruby/object:Gem::Dependency
28
+ name: activejob
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 6.0.1.rc1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 6.0.1.rc1
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: activerecord
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - '='
32
46
  - !ruby/object:Gem::Version
33
- version: 6.0.0.beta1
47
+ version: 6.0.1.rc1
34
48
  type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - '='
39
53
  - !ruby/object:Gem::Version
40
- version: 6.0.0.beta1
54
+ version: 6.0.1.rc1
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: marcel
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -132,12 +146,15 @@ files:
132
146
  - lib/active_storage/transformers/transformer.rb
133
147
  - lib/active_storage/version.rb
134
148
  - lib/tasks/activestorage.rake
135
- homepage: http://rubyonrails.org
149
+ homepage: https://rubyonrails.org
136
150
  licenses:
137
151
  - MIT
138
152
  metadata:
139
- source_code_uri: https://github.com/rails/rails/tree/v6.0.0.beta1/activestorage
140
- changelog_uri: https://github.com/rails/rails/blob/v6.0.0.beta1/activestorage/CHANGELOG.md
153
+ bug_tracker_uri: https://github.com/rails/rails/issues
154
+ changelog_uri: https://github.com/rails/rails/blob/v6.0.1.rc1/activestorage/CHANGELOG.md
155
+ documentation_uri: https://api.rubyonrails.org/v6.0.1.rc1/
156
+ mailing_list_uri: https://groups.google.com/forum/#!forum/rubyonrails-talk
157
+ source_code_uri: https://github.com/rails/rails/tree/v6.0.1.rc1/activestorage
141
158
  post_install_message:
142
159
  rdoc_options: []
143
160
  require_paths:
@@ -153,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
170
  - !ruby/object:Gem::Version
154
171
  version: 1.3.1
155
172
  requirements: []
156
- rubygems_version: 3.0.1
173
+ rubygems_version: 3.0.3
157
174
  signing_key:
158
175
  specification_version: 4
159
176
  summary: Local and cloud file storage framework.