brendon/positioning

[BUG] Items are not contracted correctly when destroying multiple items

james-reading opened this issue · 4 comments

Demonstrated with this failing test case:

  def test_destroying_multiple_items
    @first_list.items.order(:position).limit(2).destroy_all

    assert_equal 1, @third_item.reload.position
  end

I am destroying the first 2 items, so I expect the remaining item to be position 1, but it is position 2.

That's because both items are instantiated before either are destroyed. So when destroying the 2nd item, it's memoized position is still 2. Item >= position 3 are contacted, but by this point the third item is position 2, so it remains there.

Thanks for the heads up :) I had been thinking of using the uncached method to prevent this kind of issue when shuffling things around. That probably won't help here because this is the call:

def destroy_position
  contract(positioning_scope, (position + 1)..) unless destroyed_via_positioning_scope?
end

and position is just:

def position
  @positioned.send @column
end

We'd have to change that to perhaps a call like position_was: record_scope.pick(@column). It'll need some careful attention to make sure it's working properly though :) I'll put this down to work on soon :)

Hi @james-reading, please try the master branch to see if this fixes your problem. I've basically change the destruction callback to a before_. It sets the position of the item being destroyed to 0 while at the same time memoizing its original value. It then contracts the list before actually destroying the item.

Lovely stuff, that's working nicely.

Thanks @james-reading, I'll release that now :)