Class: Gitlab::RelativePositioning::ItemContext
- Inherits:
-
Object
- Object
- Gitlab::RelativePositioning::ItemContext
- Includes:
- Utils::StrongMemoize
- Defined in:
- lib/gitlab/relative_positioning/item_context.rb
Overview
This class is API private - it should not be explicitly instantiated outside of tests rubocop: disable CodeReuse/ActiveRecord
Instance Attribute Summary collapse
-
#ignoring ⇒ Object
Returns the value of attribute ignoring.
-
#model_class ⇒ Object
readonly
Returns the value of attribute model_class.
-
#object ⇒ Object
readonly
Returns the value of attribute object.
-
#range ⇒ Object
readonly
Returns the value of attribute range.
Instance Method Summary collapse
- #==(other) ⇒ Object
- #at_position(position) ⇒ Object
- #calculate_relative_position(calculation) ⇒ Object
- #create_space_left ⇒ Object
- #create_space_right ⇒ Object
- #find_next_gap(items_with_next_pos, default_end) ⇒ Object
- #find_next_gap_after ⇒ Object
- #find_next_gap_before ⇒ Object
- #grouping_column ⇒ Object
-
#initialize(object, range, ignoring: nil) ⇒ ItemContext
constructor
A new instance of ItemContext.
- #lhs_neighbour ⇒ Object
- #max_relative_position ⇒ Object
- #max_sibling ⇒ Object
- #min_relative_position ⇒ Object
- #min_sibling ⇒ Object
- #neighbour(item) ⇒ Object
- #next_relative_position ⇒ Object
- #nextify(relation, gt = true) ⇒ Object
-
#place_at_position(position, lhs) ⇒ Object
Handles the possibility that the position is already occupied by a sibling.
- #positioned? ⇒ Boolean
- #prev_relative_position ⇒ Object
- #relative_position ⇒ Object
- #relative_siblings(relation = scoped_items) ⇒ Object
- #rhs_neighbour ⇒ Object
- #scoped_items ⇒ Object
- #shift_left ⇒ Object
- #shift_right ⇒ Object
Constructor Details
#initialize(object, range, ignoring: nil) ⇒ ItemContext
Returns a new instance of ItemContext.
14 15 16 17 18 19 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 14 def initialize(object, range, ignoring: nil) @object = object @range = range @model_class = object.class @ignoring = ignoring end |
Instance Attribute Details
#ignoring ⇒ Object
Returns the value of attribute ignoring.
12 13 14 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 12 def ignoring @ignoring end |
#model_class ⇒ Object (readonly)
Returns the value of attribute model_class.
11 12 13 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 11 def model_class @model_class end |
#object ⇒ Object (readonly)
Returns the value of attribute object.
11 12 13 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 11 def object @object end |
#range ⇒ Object (readonly)
Returns the value of attribute range.
11 12 13 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 11 def range @range end |
Instance Method Details
#==(other) ⇒ Object
21 22 23 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 21 def ==(other) other.is_a?(self.class) && other.object == object && other.range == range && other.ignoring == ignoring end |
#at_position(position) ⇒ Object
117 118 119 120 121 122 123 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 117 def at_position(position) item = scoped_items.find_by(relative_position: position) raise InvalidPosition, 'No item found at the specified position' if item.nil? neighbour(item) end |
#calculate_relative_position(calculation) ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 82 def calculate_relative_position(calculation) # When calculating across projects, this is much more efficient than # MAX(relative_position) without the GROUP BY, due to index usage: # https://gitlab.com/gitlab-org/gitlab-foss/issues/54276#note_119340977 relation = scoped_items .order(Arel.sql('position').desc.nulls_last) .group(grouping_column) .limit(1) relation = yield relation if block_given? relation .pick(grouping_column, Arel.sql("#{calculation}(relative_position) AS position"))&.last end |
#create_space_left ⇒ Object
135 136 137 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 135 def create_space_left find_next_gap_before.tap { |gap| move_sequence_before(false, next_gap: gap) } end |
#create_space_right ⇒ Object
139 140 141 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 139 def create_space_right find_next_gap_after.tap { |gap| move_sequence_after(false, next_gap: gap) } end |
#find_next_gap(items_with_next_pos, default_end) ⇒ Object
161 162 163 164 165 166 167 168 169 170 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 161 def find_next_gap(items_with_next_pos, default_end) gap = model_class .from(items_with_next_pos, :items) .where('next_pos IS NULL OR ABS(pos::bigint - next_pos::bigint) >= ?', MIN_GAP) .pick(:pos, :next_pos) return if gap.nil? || gap.first == default_end Gap.new(gap.first, gap.second || default_end) end |
#find_next_gap_after ⇒ Object
152 153 154 155 156 157 158 159 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 152 def find_next_gap_after items_with_next_pos = scoped_items .select('relative_position AS pos, LEAD(relative_position) OVER (ORDER BY relative_position ASC) AS next_pos') .where('relative_position >= ?', relative_position) .order(:relative_position) find_next_gap(items_with_next_pos, range.last) end |
#find_next_gap_before ⇒ Object
143 144 145 146 147 148 149 150 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 143 def find_next_gap_before items_with_next_pos = scoped_items .select('relative_position AS pos, LEAD(relative_position) OVER (ORDER BY relative_position DESC) AS next_pos') .where('relative_position <= ?', relative_position) .order(relative_position: :desc) find_next_gap(items_with_next_pos, range.first) end |
#grouping_column ⇒ Object
97 98 99 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 97 def grouping_column model_class.relative_positioning_parent_column end |
#lhs_neighbour ⇒ Object
68 69 70 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 68 def lhs_neighbour neighbour(object.next_object_by_relative_position(ignoring: ignoring, order: :desc)) end |
#max_relative_position ⇒ Object
33 34 35 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 33 def max_relative_position strong_memoize(:max_relative_position) { calculate_relative_position('MAX') } end |
#max_sibling ⇒ Object
101 102 103 104 105 106 107 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 101 def max_sibling sib = relative_siblings .order(model_class.arel_table[:relative_position].desc.nulls_last) .first neighbour(sib) end |
#min_relative_position ⇒ Object
29 30 31 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 29 def min_relative_position strong_memoize(:min_relative_position) { calculate_relative_position('MIN') } end |
#min_sibling ⇒ Object
109 110 111 112 113 114 115 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 109 def min_sibling sib = relative_siblings .order(model_class.arel_table[:relative_position].asc.nulls_last) .first neighbour(sib) end |
#neighbour(item) ⇒ Object
76 77 78 79 80 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 76 def neighbour(item) return unless item.present? self.class.new(item, range, ignoring: ignoring) end |
#next_relative_position ⇒ Object
41 42 43 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 41 def next_relative_position calculate_relative_position('MIN') { |r| nextify(r) } if object.relative_position end |
#nextify(relation, gt = true) ⇒ Object
45 46 47 48 49 50 51 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 45 def nextify(relation, gt = true) if gt relation.where("relative_position > ?", relative_position) else relation.where("relative_position < ?", relative_position) end end |
#place_at_position(position, lhs) ⇒ Object
Handles the possibility that the position is already occupied by a sibling
58 59 60 61 62 63 64 65 66 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 58 def place_at_position(position, lhs) current_occupant = relative_siblings.find_by(relative_position: position) if current_occupant.present? Mover.new(position, range).move(object, lhs.object, current_occupant) else object.relative_position = position end end |
#positioned? ⇒ Boolean
25 26 27 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 25 def positioned? relative_position.present? end |
#prev_relative_position ⇒ Object
37 38 39 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 37 def prev_relative_position calculate_relative_position('MAX') { |r| nextify(r, false) } if object.relative_position end |
#relative_position ⇒ Object
176 177 178 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 176 def relative_position object.relative_position end |
#relative_siblings(relation = scoped_items) ⇒ Object
53 54 55 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 53 def relative_siblings(relation = scoped_items) object.exclude_self(relation) end |
#rhs_neighbour ⇒ Object
72 73 74 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 72 def rhs_neighbour neighbour(object.next_object_by_relative_position(ignoring: ignoring, order: :asc)) end |
#scoped_items ⇒ Object
172 173 174 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 172 def scoped_items object.relative_positioning_scoped_items(ignoring: ignoring) end |
#shift_left ⇒ Object
125 126 127 128 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 125 def shift_left move_sequence_before(true) object.reset_relative_position end |
#shift_right ⇒ Object
130 131 132 133 |
# File 'lib/gitlab/relative_positioning/item_context.rb', line 130 def shift_right move_sequence_after(true) object.reset_relative_position end |