diff options
-rw-r--r-- | actionmailer/lib/action_mailer/base.rb | 6 | ||||
-rw-r--r-- | actionmailer/lib/action_mailer/message_delivery.rb | 2 | ||||
-rw-r--r-- | actionmailer/lib/action_mailer/preview.rb | 8 | ||||
-rw-r--r-- | activestorage/app/models/active_storage/blob.rb | 4 | ||||
-rw-r--r-- | activestorage/app/models/active_storage/filename.rb | 18 | ||||
-rw-r--r-- | activestorage/lib/active_storage/attached.rb | 6 | ||||
-rw-r--r-- | activestorage/lib/active_storage/attached/macros.rb | 4 | ||||
-rw-r--r-- | activestorage/lib/active_storage/attached/one.rb | 24 | ||||
-rw-r--r-- | activestorage/lib/active_storage/service.rb | 2 | ||||
-rw-r--r-- | activestorage/test/models/attachments_test.rb | 25 |
10 files changed, 75 insertions, 24 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index a54eb52dcb..1c1e1a9a3b 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -59,7 +59,7 @@ module ActionMailer # The hash passed to the mail method allows you to specify any header that a <tt>Mail::Message</tt> # will accept (any valid email header including optional fields). # - # The mail method, if not passed a block, will inspect your views and send all the views with + # The +mail+ method, if not passed a block, will inspect your views and send all the views with # the same name as the method, so the above action would send the +welcome.text.erb+ view # file as well as the +welcome.html.erb+ view file in a +multipart/alternative+ email. # @@ -138,7 +138,7 @@ module ActionMailer # You can also define a <tt>default_url_options</tt> method on individual mailers to override these # default settings per-mailer. # - # By default when <tt>config.force_ssl</tt> is true, URLs generated for hosts will use the HTTPS protocol. + # By default when <tt>config.force_ssl</tt> is +true+, URLs generated for hosts will use the HTTPS protocol. # # = Sending mail # @@ -316,7 +316,7 @@ module ActionMailer # # = Callbacks # - # You can specify callbacks using before_action and after_action for configuring your messages. + # You can specify callbacks using <tt>before_action</tt> and <tt>after_action</tt> for configuring your messages. # This may be useful, for example, when you want to add default inline attachments for all # messages sent out by a certain mailer class: # diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb index fe7265834f..a2ea45dc7b 100644 --- a/actionmailer/lib/action_mailer/message_delivery.rb +++ b/actionmailer/lib/action_mailer/message_delivery.rb @@ -4,7 +4,7 @@ require "delegate" module ActionMailer # The <tt>ActionMailer::MessageDelivery</tt> class is used by - # <tt>ActionMailer::Base</tt> when creating a new mailer. + # ActionMailer::Base when creating a new mailer. # <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy # created <tt>Mail::Message</tt>. You can get direct access to the # <tt>Mail::Message</tt>, deliver the email or schedule the email to be sent diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb index 4a8d3659ec..730ac89c94 100644 --- a/actionmailer/lib/action_mailer/preview.rb +++ b/actionmailer/lib/action_mailer/preview.rb @@ -17,7 +17,7 @@ module ActionMailer # # config.action_mailer.show_previews = true # - # Defaults to true for development environment + # Defaults to +true+ for development environment # mattr_accessor :show_previews, instance_writer: false @@ -33,7 +33,7 @@ module ActionMailer # Register an Interceptor which will be called before mail is previewed. # Either a class or a string can be passed in as the Interceptor. If a - # string is passed in it will be <tt>constantize</tt>d. + # string is passed in it will be constantized. def register_preview_interceptor(interceptor) preview_interceptor = \ case interceptor @@ -81,12 +81,12 @@ module ActionMailer public_instance_methods(false).map(&:to_s).sort end - # Returns true if the email exists. + # Returns +true+ if the email exists. def email_exists?(email) emails.include?(email) end - # Returns true if the preview exists. + # Returns +true+ if the preview exists. def exists?(preview) all.any? { |p| p.preview_name == preview } end diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index 664a53a778..e6cf08ce83 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -3,8 +3,8 @@ # A blob is a record that contains the metadata about a file and a key for where that file resides on the service. # Blobs can be created in two ways: # -# 1) Subsequent to the file being uploaded server-side to the service via <tt>create_after_upload!</tt>. -# 2) Ahead of the file being directly uploaded client-side to the service via <tt>create_before_direct_upload!</tt>. +# 1. Subsequent to the file being uploaded server-side to the service via <tt>create_after_upload!</tt>. +# 2. Ahead of the file being directly uploaded client-side to the service via <tt>create_before_direct_upload!</tt>. # # The first option doesn't require any client-side JavaScript integration, and can be used by any other back-end # service that deals with files. The second option is faster, since you're not using your own server as a staging diff --git a/activestorage/app/models/active_storage/filename.rb b/activestorage/app/models/active_storage/filename.rb index 10b1116d52..dead6b6d33 100644 --- a/activestorage/app/models/active_storage/filename.rb +++ b/activestorage/app/models/active_storage/filename.rb @@ -9,25 +9,33 @@ class ActiveStorage::Filename @filename = filename end - # Filename.new("racecar.jpg").base # => "racecar" + # Returns the basename of the filename. + # + # ActiveStorage::Filename.new("racecar.jpg").base # => "racecar" def base File.basename @filename, extension_with_delimiter end - # Filename.new("racecar.jpg").extension_with_delimiter # => ".jpg" + # Returns the extension with delimiter of the filename. + # + # ActiveStorage::Filename.new("racecar.jpg").extension_with_delimiter # => ".jpg" def extension_with_delimiter File.extname @filename end - # Filename.new("racecar.jpg").extension_without_delimiter # => "jpg" + # Returns the extension without delimiter of the filename. + # + # ActiveStorage::Filename.new("racecar.jpg").extension_without_delimiter # => "jpg" def extension_without_delimiter extension_with_delimiter.from(1).to_s end alias_method :extension, :extension_without_delimiter - # Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg" - # Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg" + # Returns the sanitized filename. + # + # ActiveStorage::Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg" + # ActiveStorage::Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg" # # ...and any other character unsafe for URLs or storage is converted or stripped. def sanitized diff --git a/activestorage/lib/active_storage/attached.rb b/activestorage/lib/active_storage/attached.rb index 9c4b6d5d1d..c08fd56652 100644 --- a/activestorage/lib/active_storage/attached.rb +++ b/activestorage/lib/active_storage/attached.rb @@ -8,10 +8,10 @@ module ActiveStorage # Abstract base class for the concrete ActiveStorage::Attached::One and ActiveStorage::Attached::Many # classes that both provide proxy access to the blob association for a record. class Attached - attr_reader :name, :record + attr_reader :name, :record, :dependent - def initialize(name, record) - @name, @record = name, record + def initialize(name, record, dependent:) + @name, @record, @dependent = name, record, dependent end private diff --git a/activestorage/lib/active_storage/attached/macros.rb b/activestorage/lib/active_storage/attached/macros.rb index f3879ee2e3..35a081adc4 100644 --- a/activestorage/lib/active_storage/attached/macros.rb +++ b/activestorage/lib/active_storage/attached/macros.rb @@ -26,7 +26,7 @@ module ActiveStorage def has_one_attached(name, dependent: :purge_later) class_eval <<-CODE, __FILE__, __LINE__ + 1 def #{name} - @active_storage_attached_#{name} ||= ActiveStorage::Attached::One.new("#{name}", self) + @active_storage_attached_#{name} ||= ActiveStorage::Attached::One.new("#{name}", self, dependent: #{dependent == :purge_later ? ":purge_later" : "false"}) end CODE @@ -65,7 +65,7 @@ module ActiveStorage def has_many_attached(name, dependent: :purge_later) class_eval <<-CODE, __FILE__, __LINE__ + 1 def #{name} - @active_storage_attached_#{name} ||= ActiveStorage::Attached::Many.new("#{name}", self) + @active_storage_attached_#{name} ||= ActiveStorage::Attached::Many.new("#{name}", self, dependent: #{dependent == :purge_later ? ":purge_later" : "false"}) end CODE diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb index 2e5831348e..87c650b3d0 100644 --- a/activestorage/lib/active_storage/attached/one.rb +++ b/activestorage/lib/active_storage/attached/one.rb @@ -21,11 +21,12 @@ module ActiveStorage # person.avatar.attach(io: File.open("~/face.jpg"), filename: "face.jpg", content_type: "image/jpg") # person.avatar.attach(avatar_blob) # ActiveStorage::Blob object def attach(attachable) - write_attachment \ - ActiveStorage::Attachment.create!(record: record, name: name, blob: create_blob_from(attachable)) + purge_dependent_attachment do + write_attachment create_attachment_from(attachable) + end end - # Returns true if an attachment has been made. + # Returns +true+ if an attachment has been made. # # class User < ActiveRecord::Base # has_one_attached :avatar @@ -53,6 +54,23 @@ module ActiveStorage end private + def purge_dependent_attachment + if attached? && dependent == :purge_later + blob.tap do + transaction do + destroy + yield + end + end.purge_later + else + yield + end + end + + def create_attachment_from(attachable) + ActiveStorage::Attachment.create!(record: record, name: name, blob: create_blob_from(attachable)) + end + def write_attachment(attachment) record.public_send("#{name}_attachment=", attachment) end diff --git a/activestorage/lib/active_storage/service.rb b/activestorage/lib/active_storage/service.rb index ce736b8728..b80fdea1ab 100644 --- a/activestorage/lib/active_storage/service.rb +++ b/activestorage/lib/active_storage/service.rb @@ -77,7 +77,7 @@ module ActiveStorage raise NotImplementedError end - # Return true if a file exists at the +key+. + # Return +true+ if a file exists at the +key+. def exist?(key) raise NotImplementedError end diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb index 7cfd8683db..ac346c0087 100644 --- a/activestorage/test/models/attachments_test.rb +++ b/activestorage/test/models/attachments_test.rb @@ -31,6 +31,31 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase assert_equal "racecar.jpg", @user.avatar.filename.to_s end + test "replace attached blob" do + @user.avatar.attach create_blob(filename: "funky.jpg") + + perform_enqueued_jobs do + assert_no_difference -> { ActiveStorage::Blob.count } do + @user.avatar.attach create_blob(filename: "town.jpg") + end + end + + assert_equal "town.jpg", @user.avatar.filename.to_s + end + + test "replace attached blob unsuccessfully" do + @user.avatar.attach create_blob(filename: "funky.jpg") + + perform_enqueued_jobs do + assert_raises do + @user.avatar.attach nil + end + end + + assert_equal "funky.jpg", @user.reload.avatar.filename.to_s + assert ActiveStorage::Blob.service.exist?(@user.avatar.key) + end + test "access underlying associations of new blob" do @user.avatar.attach create_blob(filename: "funky.jpg") assert_equal @user, @user.avatar_attachment.record |