activestorage 6.0.3.2 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activestorage might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +149 -148
- data/MIT-LICENSE +1 -1
- data/README.md +35 -3
- 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/direct_uploads_controller.rb +1 -1
- 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.rb +114 -59
- 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/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.rb +5 -2
- data/lib/active_storage/analyzer.rb +6 -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/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 +77 -16
- 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.rb +3 -2
- data/lib/active_storage/previewer/mupdf_previewer.rb +3 -3
- data/lib/active_storage/previewer/poppler_pdf_previewer.rb +3 -3
- data/lib/active_storage/previewer/video_previewer.rb +2 -2
- data/lib/active_storage/service.rb +36 -7
- 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 +53 -23
- data/lib/active_storage/transformers/image_processing_transformer.rb +13 -7
- data/lib/active_storage/transformers/transformer.rb +0 -3
- metadata +56 -20
- 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
data/config/routes.rb
CHANGED
@@ -2,9 +2,13 @@
|
|
2
2
|
|
3
3
|
Rails.application.routes.draw do
|
4
4
|
scope ActiveStorage.routes_prefix do
|
5
|
-
get "/blobs/:signed_id/*filename" => "active_storage/blobs#show", as: :rails_service_blob
|
5
|
+
get "/blobs/redirect/:signed_id/*filename" => "active_storage/blobs/redirect#show", as: :rails_service_blob
|
6
|
+
get "/blobs/proxy/:signed_id/*filename" => "active_storage/blobs/proxy#show", as: :rails_service_blob_proxy
|
7
|
+
get "/blobs/:signed_id/*filename" => "active_storage/blobs/redirect#show"
|
6
8
|
|
7
|
-
get "/representations/:signed_blob_id/:variation_key/*filename" => "active_storage/representations#show", as: :rails_blob_representation
|
9
|
+
get "/representations/redirect/:signed_blob_id/:variation_key/*filename" => "active_storage/representations/redirect#show", as: :rails_blob_representation
|
10
|
+
get "/representations/proxy/:signed_blob_id/:variation_key/*filename" => "active_storage/representations/proxy#show", as: :rails_blob_representation_proxy
|
11
|
+
get "/representations/:signed_blob_id/:variation_key/*filename" => "active_storage/representations/redirect#show"
|
8
12
|
|
9
13
|
get "/disk/:encoded_key/*filename" => "active_storage/disk#show", as: :rails_disk_service
|
10
14
|
put "/disk/:encoded_token" => "active_storage/disk#update", as: :update_rails_disk_service
|
@@ -19,14 +23,60 @@ Rails.application.routes.draw do
|
|
19
23
|
route_for(:rails_blob_representation, signed_blob_id, variation_key, filename, options)
|
20
24
|
end
|
21
25
|
|
22
|
-
resolve("ActiveStorage::Variant") { |variant, options| route_for(
|
23
|
-
resolve("ActiveStorage::
|
24
|
-
|
26
|
+
resolve("ActiveStorage::Variant") { |variant, options| route_for(ActiveStorage.resolve_model_to_route, variant, options) }
|
27
|
+
resolve("ActiveStorage::VariantWithRecord") { |variant, options| route_for(ActiveStorage.resolve_model_to_route, variant, options) }
|
28
|
+
resolve("ActiveStorage::Preview") { |preview, options| route_for(ActiveStorage.resolve_model_to_route, preview, options) }
|
25
29
|
|
26
30
|
direct :rails_blob do |blob, options|
|
27
31
|
route_for(:rails_service_blob, blob.signed_id, blob.filename, options)
|
28
32
|
end
|
29
33
|
|
30
|
-
resolve("ActiveStorage::Blob") { |blob, options| route_for(
|
31
|
-
resolve("ActiveStorage::Attachment") { |attachment, options| route_for(
|
32
|
-
|
34
|
+
resolve("ActiveStorage::Blob") { |blob, options| route_for(ActiveStorage.resolve_model_to_route, blob, options) }
|
35
|
+
resolve("ActiveStorage::Attachment") { |attachment, options| route_for(ActiveStorage.resolve_model_to_route, attachment.blob, options) }
|
36
|
+
|
37
|
+
direct :rails_storage_proxy do |model, options|
|
38
|
+
if model.respond_to?(:signed_id)
|
39
|
+
route_for(
|
40
|
+
:rails_service_blob_proxy,
|
41
|
+
model.signed_id,
|
42
|
+
model.filename,
|
43
|
+
options
|
44
|
+
)
|
45
|
+
else
|
46
|
+
signed_blob_id = model.blob.signed_id
|
47
|
+
variation_key = model.variation.key
|
48
|
+
filename = model.blob.filename
|
49
|
+
|
50
|
+
route_for(
|
51
|
+
:rails_blob_representation_proxy,
|
52
|
+
signed_blob_id,
|
53
|
+
variation_key,
|
54
|
+
filename,
|
55
|
+
options
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
direct :rails_storage_redirect do |model, options|
|
61
|
+
if model.respond_to?(:signed_id)
|
62
|
+
route_for(
|
63
|
+
:rails_service_blob,
|
64
|
+
model.signed_id,
|
65
|
+
model.filename,
|
66
|
+
options
|
67
|
+
)
|
68
|
+
else
|
69
|
+
signed_blob_id = model.blob.signed_id
|
70
|
+
variation_key = model.variation.key
|
71
|
+
filename = model.blob.filename
|
72
|
+
|
73
|
+
route_for(
|
74
|
+
:rails_blob_representation,
|
75
|
+
signed_blob_id,
|
76
|
+
variation_key,
|
77
|
+
filename,
|
78
|
+
options
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end if ActiveStorage.draw_routes
|
@@ -1,13 +1,14 @@
|
|
1
1
|
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
|
2
2
|
def change
|
3
3
|
create_table :active_storage_blobs do |t|
|
4
|
-
t.string :key,
|
5
|
-
t.string :filename,
|
4
|
+
t.string :key, null: false
|
5
|
+
t.string :filename, null: false
|
6
6
|
t.string :content_type
|
7
7
|
t.text :metadata
|
8
|
-
t.
|
9
|
-
t.
|
10
|
-
t.
|
8
|
+
t.string :service_name, null: false
|
9
|
+
t.bigint :byte_size, null: false
|
10
|
+
t.string :checksum, null: false
|
11
|
+
t.datetime :created_at, null: false
|
11
12
|
|
12
13
|
t.index [ :key ], unique: true
|
13
14
|
end
|
@@ -22,5 +23,13 @@ class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
|
|
22
23
|
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
|
23
24
|
t.foreign_key :active_storage_blobs, column: :blob_id
|
24
25
|
end
|
26
|
+
|
27
|
+
create_table :active_storage_variant_records do |t|
|
28
|
+
t.belongs_to :blob, null: false, index: false
|
29
|
+
t.string :variation_digest, null: false
|
30
|
+
|
31
|
+
t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
|
32
|
+
t.foreign_key :active_storage_blobs, column: :blob_id
|
33
|
+
end
|
25
34
|
end
|
26
35
|
end
|
@@ -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
|
data/lib/active_storage.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#--
|
4
|
-
# Copyright (c) 2017-
|
4
|
+
# Copyright (c) 2017-2020 David Heinemeier Hansson, Basecamp
|
5
5
|
#
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining
|
7
7
|
# a copy of this software and associated documentation files (the
|
@@ -53,6 +53,7 @@ module ActiveStorage
|
|
53
53
|
mattr_accessor :paths, default: {}
|
54
54
|
|
55
55
|
mattr_accessor :variable_content_types, default: []
|
56
|
+
mattr_accessor :web_image_content_types, default: []
|
56
57
|
mattr_accessor :binary_content_type, default: "application/octet-stream"
|
57
58
|
mattr_accessor :content_types_to_serve_as_binary, default: []
|
58
59
|
mattr_accessor :content_types_allowed_inline, default: []
|
@@ -60,14 +61,16 @@ module ActiveStorage
|
|
60
61
|
mattr_accessor :service_urls_expire_in, default: 5.minutes
|
61
62
|
|
62
63
|
mattr_accessor :routes_prefix, default: "/rails/active_storage"
|
64
|
+
mattr_accessor :draw_routes, default: true
|
65
|
+
mattr_accessor :resolve_model_to_route, default: :rails_storage_redirect
|
63
66
|
|
64
67
|
mattr_accessor :replace_on_assign_to_many, default: false
|
68
|
+
mattr_accessor :track_variants, default: false
|
65
69
|
|
66
70
|
module Transformers
|
67
71
|
extend ActiveSupport::Autoload
|
68
72
|
|
69
73
|
autoload :Transformer
|
70
74
|
autoload :ImageProcessingTransformer
|
71
|
-
autoload :MiniMagickTransformer
|
72
75
|
end
|
73
76
|
end
|
@@ -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
|
@@ -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
|
@@ -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,10 +32,29 @@ 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
|
+
# If you need to enable +strict_loading+ to prevent lazy loading of attachment,
|
44
|
+
# pass the +:strict_loading+ option. You can do:
|
45
|
+
#
|
46
|
+
# class User < ApplicationRecord
|
47
|
+
# has_one_attached :avatar, strict_loading: true
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
def has_one_attached(name, dependent: :purge_later, service: nil, strict_loading: false)
|
51
|
+
validate_service_configuration(name, service)
|
52
|
+
|
34
53
|
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
54
|
+
# frozen_string_literal: true
|
35
55
|
def #{name}
|
36
|
-
@
|
56
|
+
@active_storage_attached ||= {}
|
57
|
+
@active_storage_attached[:#{name}] ||= ActiveStorage::Attached::One.new("#{name}", self)
|
37
58
|
end
|
38
59
|
|
39
60
|
def #{name}=(attachable)
|
@@ -46,8 +67,8 @@ module ActiveStorage
|
|
46
67
|
end
|
47
68
|
CODE
|
48
69
|
|
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
|
70
|
+
has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record, inverse_of: :record, dependent: :destroy, strict_loading: strict_loading
|
71
|
+
has_one :"#{name}_blob", through: :"#{name}_attachment", class_name: "ActiveStorage::Blob", source: :blob, strict_loading: strict_loading
|
51
72
|
|
52
73
|
scope :"with_attached_#{name}", -> { includes("#{name}_attachment": :blob) }
|
53
74
|
|
@@ -55,16 +76,19 @@ module ActiveStorage
|
|
55
76
|
|
56
77
|
after_commit(on: %i[ create update ]) { attachment_changes.delete(name.to_s).try(:upload) }
|
57
78
|
|
58
|
-
ActiveRecord::Reflection.
|
59
|
-
|
79
|
+
reflection = ActiveRecord::Reflection.create(
|
80
|
+
:has_one_attached,
|
60
81
|
name,
|
61
|
-
|
82
|
+
nil,
|
83
|
+
{ dependent: dependent, service_name: service },
|
84
|
+
self
|
62
85
|
)
|
86
|
+
ActiveRecord::Reflection.add_attachment_reflection(self, name, reflection)
|
63
87
|
end
|
64
88
|
|
65
89
|
# Specifies the relation between multiple attachments and the model.
|
66
90
|
#
|
67
|
-
# class Gallery <
|
91
|
+
# class Gallery < ApplicationRecord
|
68
92
|
# has_many_attached :photos
|
69
93
|
# end
|
70
94
|
#
|
@@ -86,10 +110,29 @@ module ActiveStorage
|
|
86
110
|
#
|
87
111
|
# If the +:dependent+ option isn't set, all the attachments will be purged
|
88
112
|
# (i.e. destroyed) whenever the record is destroyed.
|
89
|
-
|
113
|
+
#
|
114
|
+
# If you need the attachment to use a service which differs from the globally configured one,
|
115
|
+
# pass the +:service+ option. For instance:
|
116
|
+
#
|
117
|
+
# class Gallery < ActiveRecord::Base
|
118
|
+
# has_many_attached :photos, service: :s3
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# If you need to enable +strict_loading+ to prevent lazy loading of attachments,
|
122
|
+
# pass the +:strict_loading+ option. You can do:
|
123
|
+
#
|
124
|
+
# class Gallery < ApplicationRecord
|
125
|
+
# has_many_attached :photos, strict_loading: true
|
126
|
+
# end
|
127
|
+
#
|
128
|
+
def has_many_attached(name, dependent: :purge_later, service: nil, strict_loading: false)
|
129
|
+
validate_service_configuration(name, service)
|
130
|
+
|
90
131
|
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
132
|
+
# frozen_string_literal: true
|
91
133
|
def #{name}
|
92
|
-
@
|
134
|
+
@active_storage_attached ||= {}
|
135
|
+
@active_storage_attached[:#{name}] ||= ActiveStorage::Attached::Many.new("#{name}", self)
|
93
136
|
end
|
94
137
|
|
95
138
|
def #{name}=(attachables)
|
@@ -109,7 +152,7 @@ module ActiveStorage
|
|
109
152
|
end
|
110
153
|
CODE
|
111
154
|
|
112
|
-
has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record, dependent: :destroy do
|
155
|
+
has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record, dependent: :destroy, strict_loading: strict_loading do
|
113
156
|
def purge
|
114
157
|
each(&:purge)
|
115
158
|
reset
|
@@ -120,7 +163,7 @@ module ActiveStorage
|
|
120
163
|
reset
|
121
164
|
end
|
122
165
|
end
|
123
|
-
has_many :"#{name}_blobs", through: :"#{name}_attachments", class_name: "ActiveStorage::Blob", source: :blob
|
166
|
+
has_many :"#{name}_blobs", through: :"#{name}_attachments", class_name: "ActiveStorage::Blob", source: :blob, strict_loading: strict_loading
|
124
167
|
|
125
168
|
scope :"with_attached_#{name}", -> { includes("#{name}_attachments": :blob) }
|
126
169
|
|
@@ -128,12 +171,24 @@ module ActiveStorage
|
|
128
171
|
|
129
172
|
after_commit(on: %i[ create update ]) { attachment_changes.delete(name.to_s).try(:upload) }
|
130
173
|
|
131
|
-
ActiveRecord::Reflection.
|
132
|
-
|
174
|
+
reflection = ActiveRecord::Reflection.create(
|
175
|
+
:has_many_attached,
|
133
176
|
name,
|
134
|
-
|
177
|
+
nil,
|
178
|
+
{ dependent: dependent, service_name: service },
|
179
|
+
self
|
135
180
|
)
|
181
|
+
ActiveRecord::Reflection.add_attachment_reflection(self, name, reflection)
|
136
182
|
end
|
183
|
+
|
184
|
+
private
|
185
|
+
def validate_service_configuration(association_name, service)
|
186
|
+
if service.present?
|
187
|
+
ActiveStorage::Blob.services.fetch(service) do
|
188
|
+
raise ArgumentError, "Cannot configure service :#{service} for #{name}##{association_name}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
137
192
|
end
|
138
193
|
|
139
194
|
def attachment_changes #:nodoc:
|
@@ -144,6 +199,12 @@ module ActiveStorage
|
|
144
199
|
super || attachment_changes.any?
|
145
200
|
end
|
146
201
|
|
202
|
+
def initialize_dup(*) #:nodoc:
|
203
|
+
super
|
204
|
+
@active_storage_attached = nil
|
205
|
+
@attachment_changes = nil
|
206
|
+
end
|
207
|
+
|
147
208
|
def reload(*) #:nodoc:
|
148
209
|
super.tap { @attachment_changes = nil }
|
149
210
|
end
|