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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -0
- data/README.md +5 -3
- data/app/controllers/active_storage/disk_controller.rb +1 -1
- data/app/jobs/active_storage/analyze_job.rb +1 -0
- data/app/models/active_storage/attachment.rb +2 -0
- data/app/models/active_storage/blob.rb +27 -15
- data/app/models/active_storage/blob/representable.rb +5 -5
- data/app/models/active_storage/preview.rb +1 -1
- data/app/models/active_storage/variant.rb +4 -4
- data/app/models/active_storage/variation.rb +3 -3
- data/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb +3 -1
- data/lib/active_storage.rb +14 -5
- data/lib/active_storage/analyzer.rb +2 -2
- data/lib/active_storage/analyzer/image_analyzer.rb +11 -4
- data/lib/active_storage/analyzer/video_analyzer.rb +1 -1
- data/lib/active_storage/attached/changes/create_one.rb +1 -0
- data/lib/active_storage/attached/changes/delete_many.rb +4 -0
- data/lib/active_storage/attached/many.rb +1 -1
- data/lib/active_storage/attached/model.rb +12 -5
- data/lib/active_storage/downloader.rb +14 -15
- data/lib/active_storage/engine.rb +8 -0
- data/lib/active_storage/gem_version.rb +2 -2
- data/lib/active_storage/previewer.rb +4 -4
- data/lib/active_storage/previewer/poppler_pdf_previewer.rb +1 -1
- data/lib/active_storage/service.rb +4 -0
- data/lib/active_storage/service/azure_storage_service.rb +2 -2
- data/lib/active_storage/service/disk_service.rb +5 -1
- data/lib/active_storage/service/gcs_service.rb +1 -1
- data/lib/active_storage/service/mirror_service.rb +1 -1
- data/lib/active_storage/service/s3_service.rb +2 -2
- data/lib/active_storage/transformers/image_processing_transformer.rb +1 -1
- metadata +27 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d424efd1fa5c4046399c3274aca014257a2e72230feaf69e3aa7217ad65fc21
|
4
|
+
data.tar.gz: a1d549b32cd85fe13d6577ade41f2269588ab483b4a3e6c4f376a6c3ff9feeeb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84ed4b45a68bdf07bbe8b994d07338eecfe552a5fe37c98913dbcd0e5f3aa67328571de163cceac14c836533ffd1d3e06af6177614777415f110f05eab2b305c
|
7
|
+
data.tar.gz: 9c5824e5f4a5084ef5635abd5f15ecc6182f6a713cafcc4959aa6873d0c701722df149278f6bc27c7d9ac7ef2dca122595830ecb4e98d7199b7f5629c5e71f7a
|
data/CHANGELOG.md
CHANGED
@@ -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](
|
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(
|
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
|
-
*
|
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
|
6
|
+
# to the service URL.
|
7
7
|
class ActiveStorage::DiskController < ActiveStorage::BaseController
|
8
8
|
skip_forgery_protection
|
9
9
|
|
@@ -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.
|
9
|
-
#
|
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
|
-
|
67
|
-
|
68
|
-
|
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
|
71
|
-
|
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
|
169
|
-
#
|
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 +
|
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(
|
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(
|
206
|
-
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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 +
|
45
|
+
# ImageProcessing gem (such as +resize_to_limit+):
|
46
46
|
#
|
47
|
-
# avatar.variant(
|
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(
|
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
|
-
|
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
|
data/lib/active_storage.rb
CHANGED
@@ -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,
|
48
|
-
|
51
|
+
mattr_accessor :analyzers, default: []
|
52
|
+
|
49
53
|
mattr_accessor :paths, default: {}
|
50
|
-
|
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,
|
53
|
-
|
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
|
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
|
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
|
-
|
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.
|
@@ -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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
5
|
+
attr_reader :service
|
6
|
+
|
7
|
+
def initialize(service)
|
8
|
+
@service = service
|
8
9
|
end
|
9
10
|
|
10
|
-
def
|
11
|
-
open_tempfile do |file|
|
12
|
-
|
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
|
-
|
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
|
30
|
+
def download(key, file)
|
32
31
|
file.binmode
|
33
|
-
|
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 ==
|
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
|
|
@@ -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
|
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 #
|
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-",
|
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
|
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
|
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
|
@@ -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
|
-
|
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
|
@@ -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.
|
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-
|
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.
|
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.
|
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.
|
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.
|
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:
|
149
|
+
homepage: https://rubyonrails.org
|
136
150
|
licenses:
|
137
151
|
- MIT
|
138
152
|
metadata:
|
139
|
-
|
140
|
-
changelog_uri: https://github.com/rails/rails/blob/v6.0.
|
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.
|
173
|
+
rubygems_version: 3.0.3
|
157
174
|
signing_key:
|
158
175
|
specification_version: 4
|
159
176
|
summary: Local and cloud file storage framework.
|