Module: Plutonium::Positioning
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/plutonium/positioning.rb
Overview
Standalone decimal/fractional ordering. Kanban-independent.
Including this concern and calling positioned_on gives a model:
- automatic position assignment on create (appends to the end of its scope group)
reposition!(prev_record:, next_record:)for drag-and-drop reorderingbackfill_positions!class method to number existing rows
Pure math helpers are exposed as module-level methods so they can be called without an AR instance:
Plutonium::Positioning.position_between(1.0, 3.0) # => 2.0
Plutonium::Positioning.gap_exhausted?(1.0, 1.0) # => true
Defined Under Namespace
Modules: MigrationHelpers
Constant Summary collapse
- EPSILON =
1e-6
Class Method Summary collapse
-
.gap_exhausted?(prev_val, next_val) ⇒ Boolean
Returns true when
prev_valandnext_valare so close together that inserting a new midpoint would produce a duplicate. -
.position_between(prev_val, next_val) ⇒ Object
Returns the position that sits between
prev_valandnext_val.
Instance Method Summary collapse
-
#reposition!(prev_record:, next_record:) ⇒ Object
Move this record so it sits between
prev_recordandnext_recordwithin its scope group.
Class Method Details
.gap_exhausted?(prev_val, next_val) ⇒ Boolean
Returns true when prev_val and next_val are so close together
that inserting a new midpoint would produce a duplicate.
36 37 38 39 |
# File 'lib/plutonium/positioning.rb', line 36 def self.gap_exhausted?(prev_val, next_val) return false if prev_val.nil? || next_val.nil? (next_val - prev_val).abs < EPSILON end |
.position_between(prev_val, next_val) ⇒ Object
Returns the position that sits between prev_val and next_val.
Rules:
both nil → 0.0 (first item in an empty list)
prev nil → next_val - 1 (prepend)
next nil → prev_val + 1 (append)
else → midpoint
27 28 29 30 31 32 |
# File 'lib/plutonium/positioning.rb', line 27 def self.position_between(prev_val, next_val) return 0.0 if prev_val.nil? && next_val.nil? return next_val - 1 if prev_val.nil? return prev_val + 1 if next_val.nil? (prev_val + next_val) / 2.0 end |
Instance Method Details
#reposition!(prev_record:, next_record:) ⇒ Object
Move this record so it sits between prev_record and next_record
within its scope group. Pass nil for either neighbor to move to an end.
If the gap between the two neighbors is exhausted (too small to split) the scope group is rebalanced first so that fresh integer positions are available, then the record is positioned between the reloaded neighbors.
84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/plutonium/positioning.rb', line 84 def reposition!(prev_record:, next_record:) col = self.class.positioning_column prev_val = prev_record&.public_send(col) next_val = next_record&.public_send(col) if Plutonium::Positioning.gap_exhausted?(prev_val, next_val) rebalance_scope_group! prev_val = prev_record&.reload&.public_send(col) next_val = next_record&.reload&.public_send(col) end update!(col => Plutonium::Positioning.position_between(prev_val, next_val)) end |