diff --git a/lib/carrierwave/mount.rb b/lib/carrierwave/mount.rb index 16ccb3dfe..2a691af20 100644 --- a/lib/carrierwave/mount.rb +++ b/lib/carrierwave/mount.rb @@ -199,10 +199,22 @@ def store_previous_changes_for_#{column} @_previous_changes_for_#{column} = attribute_changes[_mounter(:#{column}).serialization_column] end + def reset_previous_changes_for_#{column} + # We use this variable to pass information from save time to commit time. + # Make sure this doesn't persist across multiple transactions + @_previous_changes_for_#{column} = nil + end + def remove_previously_stored_#{column} before, after = @_previous_changes_for_#{column} _mounter(:#{column}).remove_previous([before], [after]) end + + def remove_rolled_back_#{column} + before, after = @_previous_changes_for_#{column} + _mounter(:#{column}).remove_previous([after], [before]) + @_previous_changes_for_#{column} = nil + end RUBY end @@ -351,9 +363,23 @@ def store_previous_changes_for_#{column} @_previous_changes_for_#{column} = attribute_changes[_mounter(:#{column}).serialization_column] end + def reset_previous_changes_for_#{column} + # We use this variable to pass information from save time to commit time. + # Make sure this doesn't persist across multiple transactions + @_previous_changes_for_#{column} = nil + end + def remove_previously_stored_#{column} + return unless @_previous_changes_for_#{column} _mounter(:#{column}).remove_previous(*@_previous_changes_for_#{column}) end + + def remove_rolled_back_#{column} + return unless @_previous_changes_for_#{column} + before, after = @_previous_changes_for_#{column} + _mounter(:#{column}).remove_previous(after, before) + @_previous_changes_for_#{column} = nil + end RUBY end diff --git a/lib/carrierwave/orm/activerecord.rb b/lib/carrierwave/orm/activerecord.rb index 59320c13c..d8b32a18e 100644 --- a/lib/carrierwave/orm/activerecord.rb +++ b/lib/carrierwave/orm/activerecord.rb @@ -56,12 +56,15 @@ def mount_base(column, uploader=nil, options={}, &block) validates_processing_of column if uploader_option(column.to_sym, :validate_processing) validates_download_of column if uploader_option(column.to_sym, :validate_download) + after_save :"store_#{column}!" before_save :"write_#{column}_identifier" - after_save :"store_previous_changes_for_#{column}" after_commit :"remove_#{column}!", :on => :destroy after_commit :"mark_remove_#{column}_false", :on => :update + + after_save :"store_previous_changes_for_#{column}" + after_commit :"reset_previous_changes_for_#{column}" after_commit :"remove_previously_stored_#{column}", :on => :update - after_commit :"store_#{column}!", :on => [:create, :update] + after_rollback :"remove_rolled_back_#{column}" mod = Module.new prepend mod diff --git a/spec/orm/activerecord_spec.rb b/spec/orm/activerecord_spec.rb index 2b2d7834c..9b9d70222 100644 --- a/spec/orm/activerecord_spec.rb +++ b/spec/orm/activerecord_spec.rb @@ -785,6 +785,45 @@ def filename end expect(File.exist?(public_path('uploads/old.jpeg'))).to be_truthy end + + it 'should remove new file if transaction is rollback' do + Event.transaction do + @event.image = stub_file('new.jpeg') + @event.save + expect(File.exist?(public_path('uploads/new.jpeg'))).to be_truthy + raise ActiveRecord::Rollback + end + expect(File.exist?(public_path('uploads/new.jpeg'))).to be_falsey + end + + it 'should give correct url during transaction' do + Event.transaction do + @event.image = stub_file('new.jpeg') + @event.save + expect(@event.image_url).to eq '/uploads/new.jpeg' + raise ActiveRecord::Rollback + end + end + + it 'should raise error at save if storage cannot be done, preserving old' do + allow(@event).to receive(:store_image!).and_raise(CarrierWave::UploadError) + Event.transaction do + @event.image = stub_file('new.jpeg') + expect{ @event.save }.to raise_error(CarrierWave::UploadError) + raise ActiveRecord::Rollback + end + expect(File.exist?(public_path('uploads/old.jpeg'))).to be_truthy + end + + it 'should remove new file if transaction is rollback' do + Event.transaction do + @event.image = stub_file('new.jpeg') + @event.save + expect(File.exist?(public_path('uploads/new.jpeg'))).to be_truthy + raise ActiveRecord::Rollback + end + expect(File.exist?(public_path('uploads/new.jpeg'))).to be_falsey + end end describe "#mount_uploader into transaction" do