activestorage 6.0.4 → 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 -187
- 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.rb +114 -57
- 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 +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.rb +3 -2
- 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/service.rb +35 -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 +51 -23
- data/lib/active_storage/transformers/image_processing_transformer.rb +13 -7
- data/lib/active_storage/transformers/transformer.rb +0 -3
- metadata +57 -21
- 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,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
|
#
|