Class: Spree::Assets::PositionNormalizer
- Inherits:
-
Object
- Object
- Spree::Assets::PositionNormalizer
- Defined in:
- app/services/spree/assets/position_normalizer.rb
Overview
同一 viewable に属する Spree::Asset の position を、現在の並び順 (position, id) を 保ったまま 1..N の連番へ詰め直す。
管理画面の並び替え (stimulus-sortable) は、移動した 1 件だけに
asset[position] = 画面上のインデックス + 1 を送る。受け側の acts_as_list は
送られた position が既存レコードと衝突したときにしか周囲を詰め直さないため、
position が 1..N の連番でないと 200 を返すのに並び順が変わらない。
並び替えの直前と、position が壊れうる経路の直後にこのサービスを呼ぶ。
Constant Summary collapse
- BROKEN_SCOPE_CONDITION =
position が 1..N の連番になっていない viewable を検出する条件。 欠番 (最小が 1 でない / 最大が件数と一致しない) と重複 (distinct 数が件数と一致しない) を見る。
<<~SQL.squish.freeze MIN(position) <> 1 OR MAX(position) <> COUNT(*) OR COUNT(DISTINCT position) <> COUNT(*) SQL
Class Method Summary collapse
-
.broken_scopes ⇒ Array<Array(String, Integer)>
position が壊れている viewable の一覧。.
-
.call(viewable_type:, viewable_id:) ⇒ Boolean
詰め直しを行った場合 true、もともと連番だった場合 false.
Class Method Details
.broken_scopes ⇒ Array<Array(String, Integer)>
position が壊れている viewable の一覧。
44 45 46 47 48 49 50 |
# File 'app/services/spree/assets/position_normalizer.rb', line 44 def broken_scopes Spree::Asset.unscoped .where.not(viewable_id: nil) .group(:viewable_type, :viewable_id) .having(BROKEN_SCOPE_CONDITION) .pluck(:viewable_type, :viewable_id) end |
.call(viewable_type:, viewable_id:) ⇒ Boolean
Returns 詰め直しを行った場合 true、もともと連番だった場合 false.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'app/services/spree/assets/position_normalizer.rb', line 26 def call(viewable_type:, viewable_id:) return false if viewable_type.blank? || viewable_id.blank? assets = ordered_assets(viewable_type, viewable_id) return false if sequential?(assets) # 1 件ずつ書き換える途中では position が一時的に重複しうるため、 # 中断や並行読み取りに晒さないようトランザクションでまとめる。 Spree::Asset.transaction do assets.each_with_index do |asset, index| asset.update_column(:position, index + 1) unless asset.position == index + 1 end end true end |