activestorage 5.2.6.3 → 6.0.0.beta1

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.

Potentially problematic release.


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

Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -134
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +6 -5
  5. data/app/assets/javascripts/activestorage.js +4 -1
  6. data/app/controllers/active_storage/base_controller.rb +3 -5
  7. data/app/controllers/active_storage/blobs_controller.rb +1 -1
  8. data/app/controllers/active_storage/disk_controller.rb +4 -1
  9. data/app/controllers/active_storage/representations_controller.rb +1 -1
  10. data/app/controllers/concerns/active_storage/set_current.rb +15 -0
  11. data/app/javascript/activestorage/blob_record.js +6 -1
  12. data/app/jobs/active_storage/analyze_job.rb +4 -0
  13. data/app/jobs/active_storage/base_job.rb +0 -1
  14. data/app/jobs/active_storage/purge_job.rb +3 -0
  15. data/app/models/active_storage/attachment.rb +18 -9
  16. data/app/models/active_storage/blob/representable.rb +5 -5
  17. data/app/models/active_storage/blob.rb +63 -22
  18. data/app/models/active_storage/filename.rb +0 -6
  19. data/app/models/active_storage/preview.rb +3 -3
  20. data/app/models/active_storage/variant.rb +51 -52
  21. data/app/models/active_storage/variation.rb +23 -384
  22. data/config/routes.rb +13 -12
  23. data/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb +7 -0
  24. data/lib/active_storage/analyzer/video_analyzer.rb +2 -4
  25. data/lib/active_storage/analyzer.rb +9 -4
  26. data/lib/active_storage/attached/changes/create_many.rb +46 -0
  27. data/lib/active_storage/attached/changes/create_one.rb +68 -0
  28. data/lib/active_storage/attached/changes/create_one_of_many.rb +10 -0
  29. data/lib/active_storage/attached/changes/delete_many.rb +23 -0
  30. data/lib/active_storage/attached/changes/delete_one.rb +19 -0
  31. data/lib/active_storage/attached/changes.rb +16 -0
  32. data/lib/active_storage/attached/many.rb +16 -10
  33. data/lib/active_storage/attached/model.rb +140 -0
  34. data/lib/active_storage/attached/one.rb +16 -19
  35. data/lib/active_storage/attached.rb +7 -22
  36. data/lib/active_storage/downloader.rb +44 -0
  37. data/lib/active_storage/downloading.rb +8 -0
  38. data/lib/active_storage/engine.rb +36 -23
  39. data/lib/active_storage/errors.rb +22 -3
  40. data/lib/active_storage/gem_version.rb +4 -4
  41. data/lib/active_storage/previewer/poppler_pdf_previewer.rb +3 -3
  42. data/lib/active_storage/previewer/video_previewer.rb +2 -3
  43. data/lib/active_storage/previewer.rb +21 -11
  44. data/lib/active_storage/reflection.rb +64 -0
  45. data/lib/active_storage/service/azure_storage_service.rb +30 -14
  46. data/lib/active_storage/service/configurator.rb +3 -1
  47. data/lib/active_storage/service/disk_service.rb +20 -16
  48. data/lib/active_storage/service/gcs_service.rb +48 -46
  49. data/lib/active_storage/service/mirror_service.rb +1 -1
  50. data/lib/active_storage/service/s3_service.rb +10 -9
  51. data/lib/active_storage/service.rb +5 -6
  52. data/lib/active_storage/transformers/image_processing_transformer.rb +39 -0
  53. data/lib/active_storage/transformers/mini_magick_transformer.rb +38 -0
  54. data/lib/active_storage/transformers/transformer.rb +42 -0
  55. data/lib/active_storage.rb +13 -4
  56. data/lib/tasks/activestorage.rake +7 -0
  57. metadata +31 -19
  58. data/app/models/active_storage/filename/parameters.rb +0 -36
  59. data/lib/active_storage/attached/macros.rb +0 -110
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorage
4
+ class Attached::Changes::DeleteOne #:nodoc:
5
+ attr_reader :name, :record
6
+
7
+ def initialize(name, record)
8
+ @name, @record = name, record
9
+ end
10
+
11
+ def attachment
12
+ nil
13
+ end
14
+
15
+ def save
16
+ record.public_send("#{name}_attachment=", nil)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorage
4
+ module Attached::Changes #:nodoc:
5
+ extend ActiveSupport::Autoload
6
+
7
+ eager_autoload do
8
+ autoload :CreateOne
9
+ autoload :CreateMany
10
+ autoload :CreateOneOfMany
11
+
12
+ autoload :DeleteOne
13
+ autoload :DeleteMany
14
+ end
15
+ end
16
+ end
@@ -9,22 +9,29 @@ module ActiveStorage
9
9
  #
10
10
  # All methods called on this proxy object that aren't listed here will automatically be delegated to +attachments+.
11
11
  def attachments
12
- record.public_send("#{name}_attachments")
12
+ change.present? ? change.attachments : record.public_send("#{name}_attachments")
13
13
  end
14
14
 
15
- # Associates one or several attachments with the current record, saving them to the database.
15
+ # Returns all attached blobs.
16
+ def blobs
17
+ change.present? ? change.blobs : record.public_send("#{name}_blobs")
18
+ end
19
+
20
+ # Attaches one or more +attachables+ to the record.
21
+ #
22
+ # If the record is persisted and unchanged, the attachments are saved to
23
+ # the database immediately. Otherwise, they'll be saved to the DB when the
24
+ # record is next saved.
16
25
  #
17
26
  # document.images.attach(params[:images]) # Array of ActionDispatch::Http::UploadedFile objects
18
27
  # document.images.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload
19
28
  # document.images.attach(io: File.open("/path/to/racecar.jpg"), filename: "racecar.jpg", content_type: "image/jpg")
20
29
  # document.images.attach([ first_blob, second_blob ])
21
30
  def attach(*attachables)
22
- attachables.flatten.collect do |attachable|
23
- if record.new_record?
24
- attachments.build(record: record, blob: create_blob_from(attachable))
25
- else
26
- attachments.create!(record: record, blob: create_blob_from(attachable))
27
- end
31
+ if record.persisted? && !record.changed?
32
+ record.update(name => blobs + attachables.flatten)
33
+ else
34
+ record.public_send("#{name}=", blobs + attachables.flatten)
28
35
  end
29
36
  end
30
37
 
@@ -41,7 +48,7 @@ module ActiveStorage
41
48
 
42
49
  # Deletes associated attachments without purging them, leaving their respective blobs in place.
43
50
  def detach
44
- attachments.destroy_all if attached?
51
+ attachments.delete_all if attached?
45
52
  end
46
53
 
47
54
  ##
@@ -50,7 +57,6 @@ module ActiveStorage
50
57
  # Directly purges each associated attachment (i.e. destroys the blobs and
51
58
  # attachments and deletes the files on the service).
52
59
 
53
-
54
60
  ##
55
61
  # :method: purge_later
56
62
  #
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorage
4
+ # Provides the class-level DSL for declaring an Active Record model's attachments.
5
+ module Attached::Model
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ # Specifies the relation between a single attachment and the model.
10
+ #
11
+ # class User < ActiveRecord::Base
12
+ # has_one_attached :avatar
13
+ # end
14
+ #
15
+ # There is no column defined on the model side, Active Storage takes
16
+ # care of the mapping between your records and the attachment.
17
+ #
18
+ # To avoid N+1 queries, you can include the attached blobs in your query like so:
19
+ #
20
+ # User.with_attached_avatar
21
+ #
22
+ # Under the covers, this relationship is implemented as a +has_one+ association to a
23
+ # ActiveStorage::Attachment record and a +has_one-through+ association to a
24
+ # ActiveStorage::Blob record. These associations are available as +avatar_attachment+
25
+ # and +avatar_blob+. But you shouldn't need to work with these associations directly in
26
+ # most circumstances.
27
+ #
28
+ # The system has been designed to having you go through the ActiveStorage::Attached::One
29
+ # proxy that provides the dynamic proxy to the associations and factory methods, like +attach+.
30
+ #
31
+ # If the +:dependent+ option isn't set, the attachment will be purged
32
+ # (i.e. destroyed) whenever the record is destroyed.
33
+ def has_one_attached(name, dependent: :purge_later)
34
+ generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
35
+ def #{name}
36
+ @active_storage_attached_#{name} ||= ActiveStorage::Attached::One.new("#{name}", self)
37
+ end
38
+
39
+ def #{name}=(attachable)
40
+ attachment_changes["#{name}"] =
41
+ if attachable.nil?
42
+ ActiveStorage::Attached::Changes::DeleteOne.new("#{name}", self)
43
+ else
44
+ ActiveStorage::Attached::Changes::CreateOne.new("#{name}", self, attachable)
45
+ end
46
+ end
47
+ CODE
48
+
49
+ has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record, inverse_of: :record, dependent: :destroy
50
+ has_one :"#{name}_blob", through: :"#{name}_attachment", class_name: "ActiveStorage::Blob", source: :blob
51
+
52
+ scope :"with_attached_#{name}", -> { includes("#{name}_attachment": :blob) }
53
+
54
+ after_save { attachment_changes[name.to_s]&.save }
55
+
56
+ after_commit(on: %i[ create update ]) { attachment_changes.delete(name.to_s).try(:upload) }
57
+
58
+ ActiveRecord::Reflection.add_attachment_reflection(
59
+ self,
60
+ name,
61
+ ActiveRecord::Reflection.create(:has_one_attached, name, nil, { dependent: dependent }, self)
62
+ )
63
+ end
64
+
65
+ # Specifies the relation between multiple attachments and the model.
66
+ #
67
+ # class Gallery < ActiveRecord::Base
68
+ # has_many_attached :photos
69
+ # end
70
+ #
71
+ # There are no columns defined on the model side, Active Storage takes
72
+ # care of the mapping between your records and the attachments.
73
+ #
74
+ # To avoid N+1 queries, you can include the attached blobs in your query like so:
75
+ #
76
+ # Gallery.where(user: Current.user).with_attached_photos
77
+ #
78
+ # Under the covers, this relationship is implemented as a +has_many+ association to a
79
+ # ActiveStorage::Attachment record and a +has_many-through+ association to a
80
+ # ActiveStorage::Blob record. These associations are available as +photos_attachments+
81
+ # and +photos_blobs+. But you shouldn't need to work with these associations directly in
82
+ # most circumstances.
83
+ #
84
+ # The system has been designed to having you go through the ActiveStorage::Attached::Many
85
+ # proxy that provides the dynamic proxy to the associations and factory methods, like +#attach+.
86
+ #
87
+ # If the +:dependent+ option isn't set, all the attachments will be purged
88
+ # (i.e. destroyed) whenever the record is destroyed.
89
+ def has_many_attached(name, dependent: :purge_later)
90
+ generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
91
+ def #{name}
92
+ @active_storage_attached_#{name} ||= ActiveStorage::Attached::Many.new("#{name}", self)
93
+ end
94
+
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)
101
+ end
102
+ end
103
+ CODE
104
+
105
+ has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record, dependent: :destroy do
106
+ def purge
107
+ each(&:purge)
108
+ reset
109
+ end
110
+
111
+ def purge_later
112
+ each(&:purge_later)
113
+ reset
114
+ end
115
+ end
116
+ has_many :"#{name}_blobs", through: :"#{name}_attachments", class_name: "ActiveStorage::Blob", source: :blob
117
+
118
+ scope :"with_attached_#{name}", -> { includes("#{name}_attachments": :blob) }
119
+
120
+ after_save { attachment_changes[name.to_s]&.save }
121
+
122
+ after_commit(on: %i[ create update ]) { attachment_changes.delete(name.to_s).try(:upload) }
123
+
124
+ ActiveRecord::Reflection.add_attachment_reflection(
125
+ self,
126
+ name,
127
+ ActiveRecord::Reflection.create(:has_many_attached, name, nil, { dependent: dependent }, self)
128
+ )
129
+ end
130
+ end
131
+
132
+ def attachment_changes #:nodoc:
133
+ @attachment_changes ||= {}
134
+ end
135
+
136
+ def reload(*) #:nodoc:
137
+ super.tap { @attachment_changes = nil }
138
+ end
139
+ end
140
+ end
@@ -10,26 +10,28 @@ module ActiveStorage
10
10
  # You don't have to call this method to access the attachment's methods as
11
11
  # they are all available at the model level.
12
12
  def attachment
13
- record.public_send("#{name}_attachment")
13
+ change.present? ? change.attachment : record.public_send("#{name}_attachment")
14
14
  end
15
15
 
16
- # Associates a given attachment with the current record, saving it to the database.
16
+ def blank?
17
+ !attached?
18
+ end
19
+
20
+ # Attaches an +attachable+ to the record.
21
+ #
22
+ # If the record is persisted and unchanged, the attachment is saved to
23
+ # the database immediately. Otherwise, it'll be saved to the DB when the
24
+ # record is next saved.
17
25
  #
18
26
  # person.avatar.attach(params[:avatar]) # ActionDispatch::Http::UploadedFile object
19
27
  # person.avatar.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload
20
28
  # person.avatar.attach(io: File.open("/path/to/face.jpg"), filename: "face.jpg", content_type: "image/jpg")
21
29
  # person.avatar.attach(avatar_blob) # ActiveStorage::Blob object
22
30
  def attach(attachable)
23
- blob_was = blob if attached?
24
- blob = create_blob_from(attachable)
25
-
26
- unless blob == blob_was
27
- transaction do
28
- detach
29
- write_attachment build_attachment(blob: blob)
30
- end
31
-
32
- blob_was.purge_later if blob_was && dependent == :purge_later
31
+ if record.persisted? && !record.changed?
32
+ record.update(name => attachable)
33
+ else
34
+ record.public_send("#{name}=", attachable)
33
35
  end
34
36
  end
35
37
 
@@ -47,7 +49,7 @@ module ActiveStorage
47
49
  # Deletes the attachment without purging it, leaving its blob in place.
48
50
  def detach
49
51
  if attached?
50
- attachment.destroy
52
+ attachment.delete
51
53
  write_attachment nil
52
54
  end
53
55
  end
@@ -65,16 +67,11 @@ module ActiveStorage
65
67
  def purge_later
66
68
  if attached?
67
69
  attachment.purge_later
70
+ write_attachment nil
68
71
  end
69
72
  end
70
73
 
71
74
  private
72
- delegate :transaction, to: :record
73
-
74
- def build_attachment(blob:)
75
- ActiveStorage::Attachment.new(record: record, name: name, blob: blob)
76
- end
77
-
78
75
  def write_attachment(attachment)
79
76
  record.public_send("#{name}_attachment=", attachment)
80
77
  end
@@ -1,40 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "action_dispatch"
4
- require "action_dispatch/http/upload"
5
3
  require "active_support/core_ext/module/delegation"
6
4
 
7
5
  module ActiveStorage
8
6
  # Abstract base class for the concrete ActiveStorage::Attached::One and ActiveStorage::Attached::Many
9
7
  # classes that both provide proxy access to the blob association for a record.
10
8
  class Attached
11
- attr_reader :name, :record, :dependent
9
+ attr_reader :name, :record
12
10
 
13
- def initialize(name, record, dependent:)
14
- @name, @record, @dependent = name, record, dependent
11
+ def initialize(name, record)
12
+ @name, @record = name, record
15
13
  end
16
14
 
17
15
  private
18
- def create_blob_from(attachable)
19
- case attachable
20
- when ActiveStorage::Blob
21
- attachable
22
- when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
23
- ActiveStorage::Blob.create_after_upload! \
24
- io: attachable.open,
25
- filename: attachable.original_filename,
26
- content_type: attachable.content_type
27
- when Hash
28
- ActiveStorage::Blob.create_after_upload!(attachable)
29
- when String
30
- ActiveStorage::Blob.find_signed(attachable)
31
- else
32
- nil
33
- end
16
+ def change
17
+ record.attachment_changes[name]
34
18
  end
35
19
  end
36
20
  end
37
21
 
22
+ require "active_storage/attached/model"
38
23
  require "active_storage/attached/one"
39
24
  require "active_storage/attached/many"
40
- require "active_storage/attached/macros"
25
+ require "active_storage/attached/changes"
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorage
4
+ class Downloader #:nodoc:
5
+ def initialize(blob, tempdir: nil)
6
+ @blob = blob
7
+ @tempdir = tempdir
8
+ end
9
+
10
+ def download_blob_to_tempfile
11
+ open_tempfile do |file|
12
+ download_blob_to file
13
+ verify_integrity_of file
14
+ yield file
15
+ end
16
+ end
17
+
18
+ private
19
+ attr_reader :blob, :tempdir
20
+
21
+ def open_tempfile
22
+ file = Tempfile.open([ "ActiveStorage-#{blob.id}-", blob.filename.extension_with_delimiter ], tempdir)
23
+
24
+ begin
25
+ yield file
26
+ ensure
27
+ file.close!
28
+ end
29
+ end
30
+
31
+ def download_blob_to(file)
32
+ file.binmode
33
+ blob.download { |chunk| file.write(chunk) }
34
+ file.flush
35
+ file.rewind
36
+ end
37
+
38
+ def verify_integrity_of(file)
39
+ unless Digest::MD5.file(file).base64digest == blob.checksum
40
+ raise ActiveStorage::IntegrityError
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,9 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "tmpdir"
4
+ require "active_support/core_ext/string/filters"
4
5
 
5
6
  module ActiveStorage
6
7
  module Downloading
8
+ def self.included(klass)
9
+ ActiveSupport::Deprecation.warn <<~MESSAGE.squish, caller_locations(2)
10
+ ActiveStorage::Downloading is deprecated and will be removed in Active Storage 6.1.
11
+ Use ActiveStorage::Blob#open instead.
12
+ MESSAGE
13
+ end
14
+
7
15
  private
8
16
  # Opens a new tempfile in #tempdir and copies blob data into it. Yields the tempfile.
9
17
  def download_blob_to_tempfile #:doc:
@@ -10,6 +10,8 @@ require "active_storage/previewer/video_previewer"
10
10
  require "active_storage/analyzer/image_analyzer"
11
11
  require "active_storage/analyzer/video_analyzer"
12
12
 
13
+ require "active_storage/reflection"
14
+
13
15
  module ActiveStorage
14
16
  class Engine < Rails::Engine # :nodoc:
15
17
  isolate_namespace ActiveStorage
@@ -18,12 +20,15 @@ module ActiveStorage
18
20
  config.active_storage.previewers = [ ActiveStorage::Previewer::PopplerPDFPreviewer, ActiveStorage::Previewer::MuPDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ]
19
21
  config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer ]
20
22
  config.active_storage.paths = ActiveSupport::OrderedOptions.new
23
+ config.active_storage.queues = ActiveSupport::OrderedOptions.new
21
24
 
22
25
  config.active_storage.variable_content_types = %w(
23
26
  image/png
24
27
  image/gif
25
28
  image/jpg
26
29
  image/jpeg
30
+ image/pjpeg
31
+ image/tiff
27
32
  image/vnd.adobe.photoshop
28
33
  image/vnd.microsoft.icon
29
34
  )
@@ -46,39 +51,26 @@ module ActiveStorage
46
51
  image/gif
47
52
  image/jpg
48
53
  image/jpeg
54
+ image/tiff
49
55
  image/vnd.adobe.photoshop
50
56
  image/vnd.microsoft.icon
51
57
  application/pdf
52
58
  )
53
59
 
54
- default_unsupported_image_processing_arguments = %w(
55
- -debug
56
- -display
57
- -distribute-cache
58
- -help
59
- -path
60
- -print
61
- -set
62
- -verbose
63
- -version
64
- -write
65
- -write-mask
66
- )
67
-
68
60
  config.eager_load_namespaces << ActiveStorage
69
61
 
70
62
  initializer "active_storage.configs" do
71
63
  config.after_initialize do |app|
72
- ActiveStorage.logger = app.config.active_storage.logger || Rails.logger
73
- ActiveStorage.queue = app.config.active_storage.queue
74
- ActiveStorage.previewers = app.config.active_storage.previewers || []
75
- ActiveStorage.analyzers = app.config.active_storage.analyzers || []
76
- ActiveStorage.paths = app.config.active_storage.paths || {}
77
-
78
- ActiveStorage.supported_image_processing_methods = app.config.active_storage.supported_image_processing_methods || []
79
- ActiveStorage.unsupported_image_processing_arguments = app.config.active_storage.unsupported_image_processing_arguments || default_unsupported_image_processing_arguments
64
+ ActiveStorage.logger = app.config.active_storage.logger || Rails.logger
65
+ ActiveStorage.variant_processor = app.config.active_storage.variant_processor || :mini_magick
66
+ ActiveStorage.previewers = app.config.active_storage.previewers || []
67
+ ActiveStorage.analyzers = app.config.active_storage.analyzers || []
68
+ ActiveStorage.paths = app.config.active_storage.paths || {}
69
+ ActiveStorage.routes_prefix = app.config.active_storage.routes_prefix || "/rails/active_storage"
70
+
80
71
  ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || []
81
72
  ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || []
73
+ ActiveStorage.service_urls_expire_in = app.config.active_storage.service_urls_expire_in || 5.minutes
82
74
  ActiveStorage.content_types_allowed_inline = app.config.active_storage.content_types_allowed_inline || []
83
75
  ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream"
84
76
  end
@@ -88,7 +80,7 @@ module ActiveStorage
88
80
  require "active_storage/attached"
89
81
 
90
82
  ActiveSupport.on_load(:active_record) do
91
- extend ActiveStorage::Attached::Macros
83
+ include ActiveStorage::Attached::Model
92
84
  end
93
85
  end
94
86
 
@@ -124,5 +116,26 @@ module ActiveStorage
124
116
  end
125
117
  end
126
118
  end
119
+
120
+ initializer "active_storage.queues" do
121
+ config.after_initialize do |app|
122
+ if queue = app.config.active_storage.queue
123
+ ActiveSupport::Deprecation.warn \
124
+ "config.active_storage.queue is deprecated and will be removed in Rails 6.1. " \
125
+ "Set config.active_storage.queues.purge and config.active_storage.queues.analysis instead."
126
+
127
+ ActiveStorage.queues = { purge: queue, analysis: queue }
128
+ else
129
+ ActiveStorage.queues = app.config.active_storage.queues || {}
130
+ end
131
+ end
132
+ end
133
+
134
+ initializer "active_storage.reflection" do
135
+ ActiveSupport.on_load(:active_record) do
136
+ include Reflection::ActiveRecordExtensions
137
+ ActiveRecord::Reflection.singleton_class.prepend(Reflection::ReflectionExtension)
138
+ end
139
+ end
127
140
  end
128
141
  end
@@ -1,7 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorage
4
- class InvariableError < StandardError; end
5
- class UnpreviewableError < StandardError; end
6
- class UnrepresentableError < StandardError; end
4
+ # Generic base class for all Active Storage exceptions.
5
+ class Error < StandardError; end
6
+
7
+ # Raised when ActiveStorage::Blob#variant is called on a blob that isn't variable.
8
+ # Use ActiveStorage::Blob#variable? to determine whether a blob is variable.
9
+ class InvariableError < Error; end
10
+
11
+ # Raised when ActiveStorage::Blob#preview is called on a blob that isn't previewable.
12
+ # Use ActiveStorage::Blob#previewable? to determine whether a blob is previewable.
13
+ class UnpreviewableError < Error; end
14
+
15
+ # Raised when ActiveStorage::Blob#representation is called on a blob that isn't representable.
16
+ # Use ActiveStorage::Blob#representable? to determine whether a blob is representable.
17
+ class UnrepresentableError < Error; end
18
+
19
+ # Raised when uploaded or downloaded data does not match a precomputed checksum.
20
+ # Indicates that a network error or a software bug caused data corruption.
21
+ class IntegrityError < Error; end
22
+
23
+ # Raised when ActiveStorage::Blob#download is called on a blob where the
24
+ # backing file is no longer present in its service.
25
+ class FileNotFoundError < Error; end
7
26
  end
@@ -7,10 +7,10 @@ module ActiveStorage
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 5
11
- MINOR = 2
12
- TINY = 6
13
- PRE = "3"
10
+ MAJOR = 6
11
+ MINOR = 0
12
+ TINY = 0
13
+ PRE = "beta1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -12,7 +12,7 @@ module ActiveStorage
12
12
  end
13
13
 
14
14
  def pdftoppm_exists?
15
- return @pdftoppm_exists unless @pdftoppm_exists.nil?
15
+ return @pdftoppm_exists if defined?(@pdftoppm_exists)
16
16
 
17
17
  @pdftoppm_exists = system(pdftoppm_path, "-v", out: File::NULL, err: File::NULL)
18
18
  end
@@ -28,8 +28,8 @@ module ActiveStorage
28
28
 
29
29
  private
30
30
  def draw_first_page_from(file, &block)
31
- # use 72 dpi to match thumbnail dimensions of the PDF
32
- draw self.class.pdftoppm_path, "-singlefile", "-cropbox", "-r", "72", "-png", file.path, &block
31
+ # use 72 dpi to match thumbnail dimesions of the PDF
32
+ draw self.class.pdftoppm_path, "-singlefile", "-r", "72", "-png", file.path, &block
33
33
  end
34
34
  end
35
35
  end
@@ -9,15 +9,14 @@ module ActiveStorage
9
9
  def preview
10
10
  download_blob_to_tempfile do |input|
11
11
  draw_relevant_frame_from input do |output|
12
- yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png"
12
+ yield io: output, filename: "#{blob.filename.base}.jpg", content_type: "image/jpeg"
13
13
  end
14
14
  end
15
15
  end
16
16
 
17
17
  private
18
18
  def draw_relevant_frame_from(file, &block)
19
- draw ffmpeg_path, "-i", file.path, "-y", "-vcodec", "png",
20
- "-vf", "thumbnail", "-vframes", "1", "-f", "image2", "-", &block
19
+ draw ffmpeg_path, "-i", file.path, "-y", "-vframes", "1", "-f", "image2", "-", &block
21
20
  end
22
21
 
23
22
  def ffmpeg_path