-
-
Notifications
You must be signed in to change notification settings - Fork 315
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add a fast element deletion service class Deleting a collection of elements is rather slow at the moment. As elements as well as contents have a `dependent: :destroy` on them, every dependent record needs to be loaded and destroyed individually. This class uses the knowledge we have of elements, contents and essences in order to be able to destroy a collection of elements in a maximum of less than 20 database calls (if the collection of elements uses a lot of different essences, more calls happen, but there's only a limited amount of essence classes). There's one additional call to the database in here as a safeguard so we do not produce orphaned elements. It's fast, and gives a sense of security. * Use fast element deletion class in page publisher A page version's elements collection is comprised of all elements linked to a page version, so we can use the fast element deletion class introduced in the previous commit.
- Loading branch information
Showing
3 changed files
with
115 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# frozen_string_literal: true | ||
|
||
module Alchemy | ||
class DeleteElements | ||
class WouldLeaveOrphansError < StandardError; end | ||
attr_reader :elements | ||
|
||
def initialize(elements) | ||
@elements = elements | ||
end | ||
|
||
def call | ||
if orphanable_records.present? | ||
raise WouldLeaveOrphansError | ||
end | ||
|
||
contents = Alchemy::Content.where(element_id: elements.map(&:id)) | ||
contents.group_by(&:essence_type) | ||
.transform_values! { |value| value.map(&:essence_id) } | ||
.each do |class_name, ids| | ||
class_name.constantize.where(id: ids).delete_all | ||
end | ||
contents.delete_all | ||
delete_elements | ||
end | ||
|
||
private | ||
|
||
def orphanable_records | ||
Alchemy::Element.where(parent_element_id: [elements]).where.not(id: elements) | ||
end | ||
|
||
def delete_elements | ||
case elements | ||
when ActiveRecord::Associations::CollectionProxy | ||
elements.delete_all(:delete_all) | ||
when ActiveRecord::Relation | ||
elements.delete_all | ||
else | ||
Alchemy::Element.where(id: elements).delete_all | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# frozen_string_literal: true | ||
|
||
require "rails_helper" | ||
|
||
RSpec.describe Alchemy::DeleteElements do | ||
let!(:parent_element) { create(:alchemy_element, :with_nestable_elements, :with_contents) } | ||
let!(:nested_element) { parent_element.nested_elements.first } | ||
let!(:normal_element) { create(:alchemy_element, :with_contents) } | ||
|
||
before do | ||
expect(Alchemy::Element.count).not_to be_zero | ||
expect(Alchemy::Content.count).not_to be_zero | ||
expect(Alchemy::EssenceText.count).not_to be_zero | ||
expect(Alchemy::EssencePicture.count).not_to be_zero | ||
expect(Alchemy::EssenceRichtext.count).not_to be_zero | ||
end | ||
|
||
subject { Alchemy::DeleteElements.new(elements).call } | ||
|
||
context "with all elements" do | ||
let(:elements) { [parent_element, nested_element, normal_element] } | ||
|
||
it "destroys all elements" do | ||
subject | ||
expect(Alchemy::Element.count).to be_zero | ||
expect(Alchemy::Content.count).to be_zero | ||
expect(Alchemy::EssenceText.count).to be_zero | ||
expect(Alchemy::EssencePicture.count).to be_zero | ||
expect(Alchemy::EssenceRichtext.count).to be_zero | ||
end | ||
|
||
context "when calling with an ActiveRecord::Relation" do | ||
let(:elements) { Alchemy::Element.all } | ||
|
||
it "works" do | ||
subject | ||
expect(Alchemy::Element.count).to be_zero | ||
expect(Alchemy::Content.count).to be_zero | ||
expect(Alchemy::EssenceText.count).to be_zero | ||
expect(Alchemy::EssencePicture.count).to be_zero | ||
expect(Alchemy::EssenceRichtext.count).to be_zero | ||
end | ||
end | ||
|
||
context "when calling it as an association" do | ||
let(:page_version) { create(:alchemy_page_version) } | ||
let(:elements) { page_version.elements } | ||
before do | ||
Alchemy::Element.update_all(page_version_id: page_version.id) | ||
end | ||
|
||
it "works" do | ||
subject | ||
expect(Alchemy::Element.count).to be_zero | ||
expect(Alchemy::Content.count).to be_zero | ||
expect(Alchemy::EssenceText.count).to be_zero | ||
expect(Alchemy::EssencePicture.count).to be_zero | ||
expect(Alchemy::EssenceRichtext.count).to be_zero | ||
end | ||
end | ||
end | ||
|
||
context "when calling with an element having nested elements that is not in the collection" do | ||
let(:elements) { [parent_element, normal_element] } | ||
|
||
it "raises an error and deletes nothing" do | ||
expect { subject }.to raise_exception(Alchemy::DeleteElements::WouldLeaveOrphansError) | ||
end | ||
end | ||
end |