Commit bd0e3267 authored by Alex Kalderimis's avatar Alex Kalderimis

Changes to support the needs of epic trees

Hetrogenous sets of items need to override a couple of methods
parent 96e1bac3
...@@ -38,6 +38,8 @@ module RelativePositioning ...@@ -38,6 +38,8 @@ module RelativePositioning
end end
end end
# This class is API private - it should not be explicitly instantiated
# outside of tests
class ItemContext class ItemContext
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
...@@ -72,7 +74,7 @@ module RelativePositioning ...@@ -72,7 +74,7 @@ module RelativePositioning
end end
def relative_siblings(relation = scoped_items) def relative_siblings(relation = scoped_items)
relation.id_not_in(object.id) object.relative_siblings(relation)
end end
# Handles the possibility that the position is already occupied by a sibling # Handles the possibility that the position is already occupied by a sibling
...@@ -237,9 +239,7 @@ module RelativePositioning ...@@ -237,9 +239,7 @@ module RelativePositioning
def move_sequence(start_pos, end_pos, delta, include_self = false) def move_sequence(start_pos, end_pos, delta, include_self = false)
relation = include_self ? scoped_items : relative_siblings relation = include_self ? scoped_items : relative_siblings
relation object.update_relative_siblings(relation, (start_pos..end_pos), delta)
.where('relative_position BETWEEN ? AND ?', start_pos, end_pos)
.update_all("relative_position = relative_position + #{delta}")
end end
def find_next_gap_before def find_next_gap_before
...@@ -559,38 +559,47 @@ module RelativePositioning ...@@ -559,38 +559,47 @@ module RelativePositioning
end end
end end
def self.mover
Mover.new(START_POSITION, (MIN_POSITION..MAX_POSITION))
end
def move_between(before, after) def move_between(before, after)
mover = Mover.new(START_POSITION, (MIN_POSITION..MAX_POSITION))
before, after = [before, after].sort_by(&:relative_position) if before && after before, after = [before, after].sort_by(&:relative_position) if before && after
mover.move(self, before, after) RelativePositioning.mover.move(self, before, after)
end end
def move_after(before = self) def move_after(before = self)
mover = Mover.new(START_POSITION, (MIN_POSITION..MAX_POSITION)) RelativePositioning.mover.move(self, before, nil)
mover.move(self, before, nil)
end end
def move_before(after = self) def move_before(after = self)
mover = Mover.new(START_POSITION, (MIN_POSITION..MAX_POSITION)) RelativePositioning.mover.move(self, nil, after)
mover.move(self, nil, after)
end end
def move_to_end def move_to_end
mover = Mover.new(START_POSITION, (MIN_POSITION..MAX_POSITION)) RelativePositioning.mover.move_to_end(self)
mover.move_to_end(self)
rescue NoSpaceLeft rescue NoSpaceLeft
self.relative_position = MAX_POSITION self.relative_position = MAX_POSITION
end end
def move_to_start def move_to_start
mover = Mover.new(START_POSITION, (MIN_POSITION..MAX_POSITION)) RelativePositioning.mover.move_to_start(self)
mover.move_to_start(self)
rescue NoSpaceLeft rescue NoSpaceLeft
self.relative_position = MIN_POSITION self.relative_position = MIN_POSITION
end end
# This method is used during rebalancing - override it to customise the update
# logic:
def update_relative_siblings(relation, range, delta)
relation
.where(relative_position: range)
.update_all("relative_position = relative_position + #{delta}")
end
# This method is used to exclude the current self from a relation. Customize
# this if `id <> :id` is not sufficient
def relative_siblings(relation)
relation.id_not_in(id)
end
end end
...@@ -21,15 +21,11 @@ module EpicTreeSorting ...@@ -21,15 +21,11 @@ module EpicTreeSorting
included do included do
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
override :move_sequence override :update_relative_siblings
def move_sequence(start_pos, end_pos, delta, include_self = false) def update_relative_siblings(relation, range, delta)
items_to_update = scoped_items items_to_update = relation
.select(:id, :object_type) .select(:id, :object_type)
.where('relative_position BETWEEN ? AND ?', start_pos, end_pos) .where(relative_position: range)
unless include_self
items_to_update = relative_siblings(items_to_update)
end
items_to_update.group_by { |item| item.object_type }.each do |type, group_items| items_to_update.group_by { |item| item.object_type }.each do |type, group_items|
ids = group_items.map(&:id) ids = group_items.map(&:id)
...@@ -38,10 +34,8 @@ module EpicTreeSorting ...@@ -38,10 +34,8 @@ module EpicTreeSorting
end end
end end
private
override :relative_siblings override :relative_siblings
def relative_siblings(relation = scoped_items) def relative_siblings(relation)
relation.where.not('object_type = ? AND id = ?', self.class.table_name.singularize, self.id) relation.where.not('object_type = ? AND id = ?', self.class.table_name.singularize, self.id)
end end
end end
......
...@@ -106,9 +106,16 @@ RSpec.describe EpicTreeSorting do ...@@ -106,9 +106,16 @@ RSpec.describe EpicTreeSorting do
let!(:epic2) { create(:epic, parent: base_epic, group: group, relative_position: 1003) } let!(:epic2) { create(:epic, parent: base_epic, group: group, relative_position: 1003) }
let!(:epic3) { create(:epic, parent: base_epic, group: group, relative_position: 1005) } let!(:epic3) { create(:epic, parent: base_epic, group: group, relative_position: 1005) }
def move_sequence(range)
dx = 500
RelativePositioning.mover.context(item).move_sequence(range.first, range.last, dx)
end
context 'when self is an epic' do context 'when self is an epic' do
let(:item) { epic1 }
it 'moves all objects correctly' do it 'moves all objects correctly' do
epic1.move_sequence(1003, 1005, 500) move_sequence(1003..1005)
expect(epic_issue1.reload.relative_position).to eq(1000) expect(epic_issue1.reload.relative_position).to eq(1000)
expect(epic_issue2.reload.relative_position).to eq(1001) expect(epic_issue2.reload.relative_position).to eq(1001)
...@@ -121,8 +128,10 @@ RSpec.describe EpicTreeSorting do ...@@ -121,8 +128,10 @@ RSpec.describe EpicTreeSorting do
end end
context 'when self is an epic_issue' do context 'when self is an epic_issue' do
let(:item) { epic_issue1 }
it 'moves all objects correctly' do it 'moves all objects correctly' do
epic_issue1.move_sequence(1001, 1005, 500) move_sequence(1001..1005)
expect(epic_issue1.reload.relative_position).to eq(1000) expect(epic_issue1.reload.relative_position).to eq(1000)
expect(epic_issue2.reload.relative_position).to eq(1501) expect(epic_issue2.reload.relative_position).to eq(1501)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment