Module: RelativePositioning
- Extended by:
- ActiveSupport::Concern
- Includes:
- Gitlab::RelativePositioning
- Included in:
- DesignManagement::Design, Issue, WorkItems::ParentLink
- Defined in:
- app/models/concerns/relative_positioning.rb
Overview
This module makes it possible to handle items as a list, where the order of items can be easily altered Requirements:
The model must have the following named columns:
- id: integer
- relative_position: integer
The model must support a concept of siblings via a child->parent relationship, to enable rebalancing and ‘GROUP BY` in queries.
-
example: project -> issues, project is the parent relation (issues table has a parent_id column)
Two class methods must be defined when including this concern:
include RelativePositioning
# base query used for the position calculation
def self.relative_positioning_query_base(issue)
where(deleted: false)
end
# column that should be used in GROUP BY
def self.relative_positioning_parent_column
:project_id
end
Constant Summary
Constants included from Gitlab::RelativePositioning
Gitlab::RelativePositioning::IDEAL_DISTANCE, Gitlab::RelativePositioning::IllegalRange, Gitlab::RelativePositioning::InvalidPosition, Gitlab::RelativePositioning::IssuePositioningDisabled, Gitlab::RelativePositioning::MAX_GAP, Gitlab::RelativePositioning::MAX_POSITION, Gitlab::RelativePositioning::MIN_GAP, Gitlab::RelativePositioning::MIN_POSITION, Gitlab::RelativePositioning::NoSpaceLeft, Gitlab::RelativePositioning::START_POSITION, Gitlab::RelativePositioning::STEPS
Class Method Summary collapse
Instance Method Summary collapse
-
#check_repositioning_allowed! ⇒ Object
To be overriden on child classes whenever blocking position updates is necessary.
-
#could_not_move(exception) ⇒ Object
Override if you want to be notified of failures to move.
-
#exclude_self(relation, excluded: self) ⇒ Object
This method is used to exclude the current self (or another object) from a relation.
-
#model_class ⇒ Object
Override if the model class needs a more complicated computation (e.g. the object is a member of a union).
- #move_after(before = self) ⇒ Object
- #move_before(after = self) ⇒ Object
- #move_between(before, after) ⇒ Object
- #move_to_end ⇒ Object
- #move_to_start ⇒ Object
- #next_object_by_relative_position(ignoring: nil, order: :asc) ⇒ Object
- #relative_positioning_scoped_items(ignoring: nil) ⇒ Object
-
#reset_relative_position ⇒ Object
Override if the implementing class is not a simple application record, for example if the record is loaded from a union.
-
#update_relative_siblings(relation, range, delta) ⇒ Object
This method is used during rebalancing - override it to customise the update logic:.
Methods included from Gitlab::RelativePositioning
Class Method Details
.mover ⇒ Object
124 125 126 |
# File 'app/models/concerns/relative_positioning.rb', line 124 def self.mover ::Gitlab::RelativePositioning::Mover.new(START_POSITION, (MIN_POSITION..MAX_POSITION)) end |
Instance Method Details
#check_repositioning_allowed! ⇒ Object
To be overriden on child classes whenever blocking position updates is necessary.
130 131 132 |
# File 'app/models/concerns/relative_positioning.rb', line 130 def check_repositioning_allowed! nil end |
#could_not_move(exception) ⇒ Object
Override if you want to be notified of failures to move
204 205 |
# File 'app/models/concerns/relative_positioning.rb', line 204 def could_not_move(exception) end |
#exclude_self(relation, excluded: self) ⇒ Object
This method is used to exclude the current self (or another object) from a relation. Customize this if ‘id <> :id` is not sufficient
199 200 201 |
# File 'app/models/concerns/relative_positioning.rb', line 199 def exclude_self(relation, excluded: self) relation.id_not_in(excluded.id) end |
#model_class ⇒ Object
Override if the model class needs a more complicated computation (e.g. the object is a member of a union).
215 216 217 |
# File 'app/models/concerns/relative_positioning.rb', line 215 def model_class self.class end |
#move_after(before = self) ⇒ Object
143 144 145 146 147 148 |
# File 'app/models/concerns/relative_positioning.rb', line 143 def move_after(before = self) RelativePositioning.mover.move(self, before, nil) rescue NoSpaceLeft => e could_not_move(e) raise e end |
#move_before(after = self) ⇒ Object
150 151 152 153 154 155 |
# File 'app/models/concerns/relative_positioning.rb', line 150 def move_before(after = self) RelativePositioning.mover.move(self, nil, after) rescue NoSpaceLeft => e could_not_move(e) raise e end |
#move_between(before, after) ⇒ Object
134 135 136 137 138 139 140 141 |
# File 'app/models/concerns/relative_positioning.rb', line 134 def move_between(before, after) before, after = [before, after].sort_by(&:relative_position) if before && after RelativePositioning.mover.move(self, before, after) rescue NoSpaceLeft => e could_not_move(e) raise e end |
#move_to_end ⇒ Object
157 158 159 160 161 162 |
# File 'app/models/concerns/relative_positioning.rb', line 157 def move_to_end RelativePositioning.mover.move_to_end(self) rescue NoSpaceLeft => e could_not_move(e) self.relative_position = MAX_POSITION end |
#move_to_start ⇒ Object
164 165 166 167 168 169 |
# File 'app/models/concerns/relative_positioning.rb', line 164 def move_to_start RelativePositioning.mover.move_to_start(self) rescue NoSpaceLeft => e could_not_move(e) self.relative_position = MIN_POSITION end |
#next_object_by_relative_position(ignoring: nil, order: :asc) ⇒ Object
171 172 173 174 175 176 177 178 179 180 181 |
# File 'app/models/concerns/relative_positioning.rb', line 171 def next_object_by_relative_position(ignoring: nil, order: :asc) relation = relative_positioning_scoped_items(ignoring: ignoring).reorder(relative_position: order) relation = if order == :asc relation.where(self.class.arel_table[:relative_position].gt(relative_position)) else relation.where(self.class.arel_table[:relative_position].lt(relative_position)) end relation.first end |
#relative_positioning_scoped_items(ignoring: nil) ⇒ Object
183 184 185 186 187 |
# File 'app/models/concerns/relative_positioning.rb', line 183 def relative_positioning_scoped_items(ignoring: nil) relation = self.class.relative_positioning_query_base(self) relation = exclude_self(relation, excluded: ignoring) if ignoring.present? relation end |
#reset_relative_position ⇒ Object
Override if the implementing class is not a simple application record, for example if the record is loaded from a union.
209 210 211 |
# File 'app/models/concerns/relative_positioning.rb', line 209 def reset_relative_position reset.relative_position end |
#update_relative_siblings(relation, range, delta) ⇒ Object
This method is used during rebalancing - override it to customise the update logic:
191 192 193 194 195 |
# File 'app/models/concerns/relative_positioning.rb', line 191 def update_relative_siblings(relation, range, delta) relation .where(relative_position: range) .update_all("relative_position = relative_position + #{delta}") end |