Skip to content
This repository has been archived by the owner on Jul 13, 2023. It is now read-only.

DRAFT toward fixing after_post_process is run even if validations fail #2204

17 changes: 12 additions & 5 deletions lib/paperclip/attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def self.default_options
# +url+ - a relative URL of the attachment. This is interpolated using +interpolator+
# +path+ - where on the filesystem to store the attachment. This is interpolated using +interpolator+
# +styles+ - a hash of options for processing the attachment. See +has_attached_file+ for the details
# +only_process+ - style args to be run through the post-processor. This defaults to the empty list (which is
# +only_process+ - style args to be run through the post-processor. This defaults to the empty list (which is

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [113/80]

# a special case that indicates all styles should be processed)
# +default_url+ - a URL for the missing image
# +default_style+ - the style to use when an argument is not specified e.g. #url, #path
Expand Down Expand Up @@ -499,11 +499,18 @@ def process_options(options_type, style) #:nodoc:

def post_process(*style_args) #:nodoc:
return if @queued_for_write[:original].nil?

instance.run_paperclip_callbacks(:post_process) do
instance.run_paperclip_callbacks(:"#{name}_post_process") do
if !@options[:check_validity_before_processing] || !instance.errors.any?
catch(:cancel_all_after_callbacks) do
instance.run_paperclip_callbacks(:post_process) do
inner_result = instance.run_paperclip_callbacks(:"#{name}_post_process") do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [85/80]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [85/80]

# paperclip validations set instance.errors at this point...
if @options[:check_validity_before_processing] && instance.errors.any?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [82/80]

throw :cancel_all_after_callbacks
end
post_process_styles(*style_args)
true
end
unless inner_result
throw :cancel_all_after_callbacks
end
end
end
Expand Down
6 changes: 5 additions & 1 deletion lib/paperclip/callbacks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ def self.included(base)

module Defining
def define_paperclip_callbacks(*callbacks)
define_callbacks(*[callbacks, { terminator: hasta_la_vista_baby }].flatten)
options = {
terminator: hasta_la_vista_baby,
skip_after_callbacks_if_terminated: true,
}
define_callbacks(*[callbacks, options].flatten)
callbacks.each do |callback|
eval <<-end_callbacks
def before_#{callback}(*args, &blk)
Expand Down
56 changes: 56 additions & 0 deletions spec/paperclip/attachment_callbacks_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require 'spec_helper'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.


describe Paperclip::Attachment do
context "callbacks" do
context "with content_type validation" do
def rebuild(required_content_type)
rebuild_class styles: { something: "100x100#" }
Dummy.class_eval do
validates_attachment_content_type :avatar, content_type: [required_content_type]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [90/80]

before_avatar_post_process :do_before_avatar
after_avatar_post_process :do_after_avatar
before_post_process :do_before_all
after_post_process :do_after_all
def do_before_avatar ; end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space found before semicolon.

def do_after_avatar; end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use empty lines between method definitions.

def do_before_all; end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use empty lines between method definitions.

def do_after_all; end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use empty lines between method definitions.

end
@dummy = Dummy.new
end

context "that passes" do
let!(:required_content_type) { "image/png" }
before { rebuild(required_content_type) }
let(:fake_file) { StringIO.new(".").tap { |s| s.stubs(:to_tempfile).returns(s) } }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [91/80]
Unnecessary spacing detected.


it "calls all callbacks when assigned" do
@dummy.expects(:do_before_avatar).with()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use parentheses for method calls with no arguments.

@dummy.expects(:do_after_avatar).with()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use parentheses for method calls with no arguments.

@dummy.expects(:do_before_all).with()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use parentheses for method calls with no arguments.

@dummy.expects(:do_after_all).with()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use parentheses for method calls with no arguments.

Paperclip::Thumbnail.expects(:make).returns(fake_file)
@dummy.avatar = File.new(fixture_file("5k.png"))
end
end

context "that fails" do
let!(:required_content_type) { "image/jpeg" }
before { rebuild(required_content_type) }

it "does not call after callbacks when assigned" do
# before callbacks ARE still called at present
@dummy.expects(:do_before_all).with()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use parentheses for method calls with no arguments.

@dummy.expects(:do_before_avatar).with()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use parentheses for method calls with no arguments.


# But after_* are not
@dummy.expects(:do_after_avatar).never
@dummy.expects(:do_after_all).never
Paperclip::Thumbnail.expects(:make).never

@dummy.avatar = File.new(fixture_file("5k.png"))
end
end
end
end
end
6 changes: 3 additions & 3 deletions spec/paperclip/attachment_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -847,16 +847,16 @@ def do_after_all; end
@dummy.expects(:do_before_avatar).never
@dummy.expects(:do_after_avatar).never
@dummy.expects(:do_before_all).with().returns(false)
@dummy.expects(:do_after_all)
@dummy.expects(:do_after_all).never
Paperclip::Thumbnail.expects(:make).never
@dummy.avatar = @file
end

it "cancels the processing if a before_avatar_post_process returns false" do
@dummy.expects(:do_before_avatar).with().returns(false)
@dummy.expects(:do_after_avatar)
@dummy.expects(:do_after_avatar).never
@dummy.expects(:do_before_all).with().returns(true)
@dummy.expects(:do_after_all)
@dummy.expects(:do_after_all).never
Paperclip::Thumbnail.expects(:make).never
@dummy.avatar = @file
end
Expand Down