activestorage 6.0.4.1 → 6.1.0.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 +141 -192
- data/MIT-LICENSE +1 -1
- data/README.md +36 -4
- data/app/controllers/active_storage/base_controller.rb +11 -0
- data/app/controllers/active_storage/blobs/proxy_controller.rb +14 -0
- data/app/controllers/active_storage/{blobs_controller.rb → blobs/redirect_controller.rb} +2 -2
- data/app/controllers/active_storage/disk_controller.rb +8 -20
- data/app/controllers/active_storage/representations/proxy_controller.rb +19 -0
- data/app/controllers/active_storage/{representations_controller.rb → representations/redirect_controller.rb} +2 -2
- data/app/controllers/concerns/active_storage/file_server.rb +18 -0
- data/app/controllers/concerns/active_storage/set_blob.rb +1 -1
- data/app/controllers/concerns/active_storage/set_current.rb +2 -2
- data/app/controllers/concerns/active_storage/set_headers.rb +12 -0
- data/app/jobs/active_storage/mirror_job.rb +15 -0
- data/app/models/active_storage/attachment.rb +18 -10
- data/app/models/active_storage/blob/analyzable.rb +6 -2
- data/app/models/active_storage/blob/identifiable.rb +7 -6
- data/app/models/active_storage/blob/representable.rb +34 -4
- data/app/models/active_storage/blob.rb +114 -57
- data/app/models/active_storage/preview.rb +31 -10
- data/app/models/active_storage/record.rb +7 -0
- data/app/models/active_storage/variant.rb +28 -41
- data/app/models/active_storage/variant_record.rb +8 -0
- data/app/models/active_storage/variant_with_record.rb +54 -0
- data/app/models/active_storage/variation.rb +25 -20
- data/config/routes.rb +58 -8
- data/db/migrate/20170806125915_create_active_storage_tables.rb +14 -5
- data/db/update_migrate/20190112182829_add_service_name_to_active_storage_blobs.rb +17 -0
- data/db/update_migrate/20191206030411_create_active_storage_variant_records.rb +11 -0
- data/lib/active_storage/analyzer/image_analyzer.rb +3 -0
- data/lib/active_storage/analyzer/null_analyzer.rb +4 -0
- data/lib/active_storage/analyzer/video_analyzer.rb +14 -3
- data/lib/active_storage/analyzer.rb +6 -0
- data/lib/active_storage/attached/changes/create_many.rb +1 -0
- data/lib/active_storage/attached/changes/create_one.rb +17 -4
- data/lib/active_storage/attached/many.rb +4 -3
- data/lib/active_storage/attached/model.rb +49 -10
- data/lib/active_storage/attached/one.rb +4 -3
- data/lib/active_storage/engine.rb +25 -27
- data/lib/active_storage/gem_version.rb +3 -3
- data/lib/active_storage/log_subscriber.rb +6 -0
- data/lib/active_storage/previewer/mupdf_previewer.rb +3 -3
- data/lib/active_storage/previewer/poppler_pdf_previewer.rb +2 -2
- data/lib/active_storage/previewer/video_previewer.rb +2 -2
- data/lib/active_storage/previewer.rb +3 -2
- data/lib/active_storage/service/azure_storage_service.rb +40 -35
- data/lib/active_storage/service/configurator.rb +3 -1
- data/lib/active_storage/service/disk_service.rb +36 -31
- data/lib/active_storage/service/gcs_service.rb +18 -16
- data/lib/active_storage/service/mirror_service.rb +31 -7
- data/lib/active_storage/service/registry.rb +32 -0
- data/lib/active_storage/service/s3_service.rb +51 -23
- data/lib/active_storage/service.rb +35 -7
- data/lib/active_storage/transformers/image_processing_transformer.rb +13 -7
- data/lib/active_storage/transformers/transformer.rb +0 -3
- data/lib/active_storage.rb +5 -2
- metadata +60 -24
- data/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb +0 -9
- data/lib/active_storage/downloading.rb +0 -47
- data/lib/active_storage/transformers/mini_magick_transformer.rb +0 -38
@@ -0,0 +1,17 @@
|
|
1
|
+
class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
|
2
|
+
def up
|
3
|
+
unless column_exists?(:active_storage_blobs, :service_name)
|
4
|
+
add_column :active_storage_blobs, :service_name, :string
|
5
|
+
|
6
|
+
if configured_service = ActiveStorage::Blob.service.name
|
7
|
+
ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
|
8
|
+
end
|
9
|
+
|
10
|
+
change_column :active_storage_blobs, :service_name, :string, null: false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def down
|
15
|
+
remove_column :active_storage_blobs, :service_name
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :active_storage_variant_records do |t|
|
4
|
+
t.belongs_to :blob, null: false, index: false
|
5
|
+
t.string :variation_digest, null: false
|
6
|
+
|
7
|
+
t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
|
8
|
+
t.foreign_key :active_storage_blobs, column: :blob_id
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -43,6 +43,9 @@ module ActiveStorage
|
|
43
43
|
rescue LoadError
|
44
44
|
logger.info "Skipping image analysis because the mini_magick gem isn't installed"
|
45
45
|
{}
|
46
|
+
rescue MiniMagick::Error => error
|
47
|
+
logger.error "Skipping image analysis due to an ImageMagick error: #{error.message}"
|
48
|
+
{}
|
46
49
|
end
|
47
50
|
|
48
51
|
def rotated_image?(image)
|
@@ -44,7 +44,8 @@ module ActiveStorage
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def duration
|
47
|
-
|
47
|
+
duration = video_stream["duration"] || container["duration"]
|
48
|
+
Float(duration) if duration
|
48
49
|
end
|
49
50
|
|
50
51
|
def angle
|
@@ -98,12 +99,22 @@ module ActiveStorage
|
|
98
99
|
probe["streams"] || []
|
99
100
|
end
|
100
101
|
|
102
|
+
def container
|
103
|
+
probe["format"] || {}
|
104
|
+
end
|
105
|
+
|
101
106
|
def probe
|
102
|
-
download_blob_to_tempfile { |file| probe_from(file) }
|
107
|
+
@probe ||= download_blob_to_tempfile { |file| probe_from(file) }
|
103
108
|
end
|
104
109
|
|
105
110
|
def probe_from(file)
|
106
|
-
IO.popen([ ffprobe_path,
|
111
|
+
IO.popen([ ffprobe_path,
|
112
|
+
"-print_format", "json",
|
113
|
+
"-show_streams",
|
114
|
+
"-show_format",
|
115
|
+
"-v", "error",
|
116
|
+
file.path
|
117
|
+
]) do |output|
|
107
118
|
JSON.parse(output.read)
|
108
119
|
end
|
109
120
|
rescue Errno::ENOENT
|
@@ -12,6 +12,12 @@ module ActiveStorage
|
|
12
12
|
false
|
13
13
|
end
|
14
14
|
|
15
|
+
# Implement this method in concrete subclasses. It will determine if blob analysis
|
16
|
+
# should be done in a job or performed inline. By default, analysis is enqueued in a job.
|
17
|
+
def self.analyze_later?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
15
21
|
def initialize(blob)
|
16
22
|
@blob = blob
|
17
23
|
end
|
@@ -9,6 +9,7 @@ module ActiveStorage
|
|
9
9
|
|
10
10
|
def initialize(name, record, attachable)
|
11
11
|
@name, @record, @attachable = name, record, attachable
|
12
|
+
blob.identify_without_saving
|
12
13
|
end
|
13
14
|
|
14
15
|
def attachment
|
@@ -53,17 +54,29 @@ module ActiveStorage
|
|
53
54
|
when ActiveStorage::Blob
|
54
55
|
attachable
|
55
56
|
when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
|
56
|
-
ActiveStorage::Blob.build_after_unfurling
|
57
|
+
ActiveStorage::Blob.build_after_unfurling(
|
57
58
|
io: attachable.open,
|
58
59
|
filename: attachable.original_filename,
|
59
|
-
content_type: attachable.content_type
|
60
|
+
content_type: attachable.content_type,
|
61
|
+
record: record,
|
62
|
+
service_name: attachment_service_name
|
63
|
+
)
|
60
64
|
when Hash
|
61
|
-
ActiveStorage::Blob.build_after_unfurling(
|
65
|
+
ActiveStorage::Blob.build_after_unfurling(
|
66
|
+
**attachable.reverse_merge(
|
67
|
+
record: record,
|
68
|
+
service_name: attachment_service_name
|
69
|
+
).symbolize_keys
|
70
|
+
)
|
62
71
|
when String
|
63
|
-
ActiveStorage::Blob.find_signed(attachable)
|
72
|
+
ActiveStorage::Blob.find_signed!(attachable, record: record)
|
64
73
|
else
|
65
74
|
raise ArgumentError, "Could not find or build blob: expected attachable, got #{attachable.inspect}"
|
66
75
|
end
|
67
76
|
end
|
77
|
+
|
78
|
+
def attachment_service_name
|
79
|
+
record.attachment_reflections[name].options[:service_name]
|
80
|
+
end
|
68
81
|
end
|
69
82
|
end
|
@@ -29,15 +29,16 @@ module ActiveStorage
|
|
29
29
|
# document.images.attach([ first_blob, second_blob ])
|
30
30
|
def attach(*attachables)
|
31
31
|
if record.persisted? && !record.changed?
|
32
|
-
record.
|
32
|
+
record.public_send("#{name}=", blobs + attachables.flatten)
|
33
|
+
record.save
|
33
34
|
else
|
34
35
|
record.public_send("#{name}=", (change&.attachables || blobs) + attachables.flatten)
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
38
|
-
# Returns true if any attachments
|
39
|
+
# Returns true if any attachments have been made.
|
39
40
|
#
|
40
|
-
# class Gallery <
|
41
|
+
# class Gallery < ApplicationRecord
|
41
42
|
# has_many_attached :photos
|
42
43
|
# end
|
43
44
|
#
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/object/try"
|
4
|
+
|
3
5
|
module ActiveStorage
|
4
6
|
# Provides the class-level DSL for declaring an Active Record model's attachments.
|
5
7
|
module Attached::Model
|
@@ -8,7 +10,7 @@ module ActiveStorage
|
|
8
10
|
class_methods do
|
9
11
|
# Specifies the relation between a single attachment and the model.
|
10
12
|
#
|
11
|
-
# class User <
|
13
|
+
# class User < ApplicationRecord
|
12
14
|
# has_one_attached :avatar
|
13
15
|
# end
|
14
16
|
#
|
@@ -30,8 +32,19 @@ module ActiveStorage
|
|
30
32
|
#
|
31
33
|
# If the +:dependent+ option isn't set, the attachment will be purged
|
32
34
|
# (i.e. destroyed) whenever the record is destroyed.
|
33
|
-
|
35
|
+
#
|
36
|
+
# If you need the attachment to use a service which differs from the globally configured one,
|
37
|
+
# pass the +:service+ option. For instance:
|
38
|
+
#
|
39
|
+
# class User < ActiveRecord::Base
|
40
|
+
# has_one_attached :avatar, service: :s3
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
def has_one_attached(name, dependent: :purge_later, service: nil)
|
44
|
+
validate_service_configuration(name, service)
|
45
|
+
|
34
46
|
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
47
|
+
# frozen_string_literal: true
|
35
48
|
def #{name}
|
36
49
|
@active_storage_attached ||= {}
|
37
50
|
@active_storage_attached[:#{name}] ||= ActiveStorage::Attached::One.new("#{name}", self)
|
@@ -56,16 +69,19 @@ module ActiveStorage
|
|
56
69
|
|
57
70
|
after_commit(on: %i[ create update ]) { attachment_changes.delete(name.to_s).try(:upload) }
|
58
71
|
|
59
|
-
ActiveRecord::Reflection.
|
60
|
-
|
72
|
+
reflection = ActiveRecord::Reflection.create(
|
73
|
+
:has_one_attached,
|
61
74
|
name,
|
62
|
-
|
75
|
+
nil,
|
76
|
+
{ dependent: dependent, service_name: service },
|
77
|
+
self
|
63
78
|
)
|
79
|
+
ActiveRecord::Reflection.add_attachment_reflection(self, name, reflection)
|
64
80
|
end
|
65
81
|
|
66
82
|
# Specifies the relation between multiple attachments and the model.
|
67
83
|
#
|
68
|
-
# class Gallery <
|
84
|
+
# class Gallery < ApplicationRecord
|
69
85
|
# has_many_attached :photos
|
70
86
|
# end
|
71
87
|
#
|
@@ -87,8 +103,19 @@ module ActiveStorage
|
|
87
103
|
#
|
88
104
|
# If the +:dependent+ option isn't set, all the attachments will be purged
|
89
105
|
# (i.e. destroyed) whenever the record is destroyed.
|
90
|
-
|
106
|
+
#
|
107
|
+
# If you need the attachment to use a service which differs from the globally configured one,
|
108
|
+
# pass the +:service+ option. For instance:
|
109
|
+
#
|
110
|
+
# class Gallery < ActiveRecord::Base
|
111
|
+
# has_many_attached :photos, service: :s3
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
def has_many_attached(name, dependent: :purge_later, service: nil)
|
115
|
+
validate_service_configuration(name, service)
|
116
|
+
|
91
117
|
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
118
|
+
# frozen_string_literal: true
|
92
119
|
def #{name}
|
93
120
|
@active_storage_attached ||= {}
|
94
121
|
@active_storage_attached[:#{name}] ||= ActiveStorage::Attached::Many.new("#{name}", self)
|
@@ -130,12 +157,24 @@ module ActiveStorage
|
|
130
157
|
|
131
158
|
after_commit(on: %i[ create update ]) { attachment_changes.delete(name.to_s).try(:upload) }
|
132
159
|
|
133
|
-
ActiveRecord::Reflection.
|
134
|
-
|
160
|
+
reflection = ActiveRecord::Reflection.create(
|
161
|
+
:has_many_attached,
|
135
162
|
name,
|
136
|
-
|
163
|
+
nil,
|
164
|
+
{ dependent: dependent, service_name: service },
|
165
|
+
self
|
137
166
|
)
|
167
|
+
ActiveRecord::Reflection.add_attachment_reflection(self, name, reflection)
|
138
168
|
end
|
169
|
+
|
170
|
+
private
|
171
|
+
def validate_service_configuration(association_name, service)
|
172
|
+
if service.present?
|
173
|
+
ActiveStorage::Blob.services.fetch(service) do
|
174
|
+
raise ArgumentError, "Cannot configure service :#{service} for #{name}##{association_name}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
139
178
|
end
|
140
179
|
|
141
180
|
def attachment_changes #:nodoc:
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveStorage
|
4
4
|
# Representation of a single attachment to a model.
|
5
5
|
class Attached::One < Attached
|
6
|
-
delegate_missing_to :attachment
|
6
|
+
delegate_missing_to :attachment, allow_nil: true
|
7
7
|
|
8
8
|
# Returns the associated attachment record.
|
9
9
|
#
|
@@ -29,7 +29,8 @@ module ActiveStorage
|
|
29
29
|
# person.avatar.attach(avatar_blob) # ActiveStorage::Blob object
|
30
30
|
def attach(attachable)
|
31
31
|
if record.persisted? && !record.changed?
|
32
|
-
record.
|
32
|
+
record.public_send("#{name}=", attachable)
|
33
|
+
record.save
|
33
34
|
else
|
34
35
|
record.public_send("#{name}=", attachable)
|
35
36
|
end
|
@@ -37,7 +38,7 @@ module ActiveStorage
|
|
37
38
|
|
38
39
|
# Returns +true+ if an attachment has been made.
|
39
40
|
#
|
40
|
-
# class User <
|
41
|
+
# class User < ApplicationRecord
|
41
42
|
# has_one_attached :avatar
|
42
43
|
# end
|
43
44
|
#
|
@@ -14,6 +14,8 @@ require "active_storage/previewer/video_previewer"
|
|
14
14
|
require "active_storage/analyzer/image_analyzer"
|
15
15
|
require "active_storage/analyzer/video_analyzer"
|
16
16
|
|
17
|
+
require "active_storage/service/registry"
|
18
|
+
|
17
19
|
require "active_storage/reflection"
|
18
20
|
|
19
21
|
module ActiveStorage
|
@@ -24,7 +26,7 @@ module ActiveStorage
|
|
24
26
|
config.active_storage.previewers = [ ActiveStorage::Previewer::PopplerPDFPreviewer, ActiveStorage::Previewer::MuPDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ]
|
25
27
|
config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer ]
|
26
28
|
config.active_storage.paths = ActiveSupport::OrderedOptions.new
|
27
|
-
config.active_storage.queues = ActiveSupport::
|
29
|
+
config.active_storage.queues = ActiveSupport::InheritableOptions.new(mirror: :active_storage_mirror)
|
28
30
|
|
29
31
|
config.active_storage.variable_content_types = %w(
|
30
32
|
image/png
|
@@ -36,6 +38,14 @@ module ActiveStorage
|
|
36
38
|
image/bmp
|
37
39
|
image/vnd.adobe.photoshop
|
38
40
|
image/vnd.microsoft.icon
|
41
|
+
image/webp
|
42
|
+
)
|
43
|
+
|
44
|
+
config.active_storage.web_image_content_types = %w(
|
45
|
+
image/png
|
46
|
+
image/jpeg
|
47
|
+
image/jpg
|
48
|
+
image/gif
|
39
49
|
)
|
40
50
|
|
41
51
|
config.active_storage.content_types_to_serve_as_binary = %w(
|
@@ -73,14 +83,18 @@ module ActiveStorage
|
|
73
83
|
ActiveStorage.analyzers = app.config.active_storage.analyzers || []
|
74
84
|
ActiveStorage.paths = app.config.active_storage.paths || {}
|
75
85
|
ActiveStorage.routes_prefix = app.config.active_storage.routes_prefix || "/rails/active_storage"
|
86
|
+
ActiveStorage.draw_routes = app.config.active_storage.draw_routes != false
|
87
|
+
ActiveStorage.resolve_model_to_route = app.config.active_storage.resolve_model_to_route || :rails_storage_redirect
|
76
88
|
|
77
89
|
ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || []
|
90
|
+
ActiveStorage.web_image_content_types = app.config.active_storage.web_image_content_types || []
|
78
91
|
ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || []
|
79
92
|
ActiveStorage.service_urls_expire_in = app.config.active_storage.service_urls_expire_in || 5.minutes
|
80
93
|
ActiveStorage.content_types_allowed_inline = app.config.active_storage.content_types_allowed_inline || []
|
81
94
|
ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream"
|
82
95
|
|
83
96
|
ActiveStorage.replace_on_assign_to_many = app.config.active_storage.replace_on_assign_to_many || false
|
97
|
+
ActiveStorage.track_variants = app.config.active_storage.track_variants || false
|
84
98
|
end
|
85
99
|
end
|
86
100
|
|
@@ -100,42 +114,26 @@ module ActiveStorage
|
|
100
114
|
|
101
115
|
initializer "active_storage.services" do
|
102
116
|
ActiveSupport.on_load(:active_storage_blob) do
|
103
|
-
|
104
|
-
|
105
|
-
config_file =
|
117
|
+
configs = Rails.configuration.active_storage.service_configurations ||=
|
118
|
+
begin
|
119
|
+
config_file = Rails.root.join("config/storage/#{Rails.env}.yml")
|
120
|
+
config_file = Rails.root.join("config/storage.yml") unless config_file.exist?
|
106
121
|
raise("Couldn't find Active Storage configuration in #{config_file}") unless config_file.exist?
|
107
122
|
|
108
|
-
|
109
|
-
require "erb"
|
110
|
-
|
111
|
-
YAML.load(ERB.new(config_file.read).result) || {}
|
112
|
-
rescue Psych::SyntaxError => e
|
113
|
-
raise "YAML syntax error occurred while parsing #{config_file}. " \
|
114
|
-
"Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
|
115
|
-
"Error: #{e.message}"
|
123
|
+
ActiveSupport::ConfigurationFile.parse(config_file)
|
116
124
|
end
|
117
125
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
raise e, "Cannot load `Rails.config.active_storage.service`:\n#{e.message}", e.backtrace
|
123
|
-
end
|
126
|
+
ActiveStorage::Blob.services = ActiveStorage::Service::Registry.new(configs)
|
127
|
+
|
128
|
+
if config_choice = Rails.configuration.active_storage.service
|
129
|
+
ActiveStorage::Blob.service = ActiveStorage::Blob.services.fetch(config_choice)
|
124
130
|
end
|
125
131
|
end
|
126
132
|
end
|
127
133
|
|
128
134
|
initializer "active_storage.queues" do
|
129
135
|
config.after_initialize do |app|
|
130
|
-
|
131
|
-
ActiveSupport::Deprecation.warn \
|
132
|
-
"config.active_storage.queue is deprecated and will be removed in Rails 6.1. " \
|
133
|
-
"Set config.active_storage.queues.purge and config.active_storage.queues.analysis instead."
|
134
|
-
|
135
|
-
ActiveStorage.queues = { purge: queue, analysis: queue }
|
136
|
-
else
|
137
|
-
ActiveStorage.queues = app.config.active_storage.queues || {}
|
138
|
-
end
|
136
|
+
ActiveStorage.queues = app.config.active_storage.queues || {}
|
139
137
|
end
|
140
138
|
end
|
141
139
|
|
@@ -32,6 +32,12 @@ module ActiveStorage
|
|
32
32
|
debug event, color("Generated URL for file at key: #{key_in(event)} (#{event.payload[:url]})", BLUE)
|
33
33
|
end
|
34
34
|
|
35
|
+
def service_mirror(event)
|
36
|
+
message = "Mirrored file at key: #{key_in(event)}"
|
37
|
+
message += " (checksum: #{event.payload[:checksum]})" if event.payload[:checksum]
|
38
|
+
debug event, color(message, GREEN)
|
39
|
+
end
|
40
|
+
|
35
41
|
def logger
|
36
42
|
ActiveStorage.logger
|
37
43
|
end
|
@@ -12,7 +12,7 @@ module ActiveStorage
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def mutool_exists?
|
15
|
-
return @mutool_exists
|
15
|
+
return @mutool_exists if defined?(@mutool_exists) && !@mutool_exists.nil?
|
16
16
|
|
17
17
|
system mutool_path, out: File::NULL, err: File::NULL
|
18
18
|
|
@@ -20,10 +20,10 @@ module ActiveStorage
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def preview
|
23
|
+
def preview(**options)
|
24
24
|
download_blob_to_tempfile do |input|
|
25
25
|
draw_first_page_from input do |output|
|
26
|
-
yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png"
|
26
|
+
yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png", **options
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|