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

15 changes: 10 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,12 +499,17 @@ 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...
unless !@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. [88/80]

Copy link
Contributor

Choose a reason for hiding this comment

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

can we switch to the positive case for this?

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, I just got confused what the logic was, so seemed safer to just switch the previous 'if' to an 'unless'. But I can pull out my DeMorgan's chart, heh.

throw :cancel_all_after_callbacks
end
post_process_styles(*style_args)
true
end
throw :cancel_all_after_callbacks unless inner_result
Copy link
Contributor

Choose a reason for hiding this comment

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

thoughts on moving the conditional to wrap the throw?

Copy link
Author

Choose a reason for hiding this comment

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

sure, if you think that's better I can do it. You just mean:

 unless inner_result
   throw :cancel_all_after_callbacks
 end

okay!

Copy link
Contributor

Choose a reason for hiding this comment

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

@jrochkind yep, exactly - I might even go so far as to suggest switching to a if !inner_result. The trailing conditional is really what we want to avoid though.

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