brendon / acts_as_list

An ActiveRecord plugin for managing lists.

Home Page:http://brendon.github.io/acts_as_list/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

acts_as_list_no_update does not work with nested blocks.

conorbdaly opened this issue · comments

I encountered this issue when trying to copy a parent - child structure while still preserving positioning.

The code would have been similar to:

::Parents.acts_as_list_no_update do
  Parents.all.each do |parent|
    copy = parent.dup
    copy.save!
    
    ::Children.acts_as_list_no_update do
      parent.children.each do |child|
        child_copy = child.dup
        copy.children << child_copy
        child_copy.save!
      end
    end
  end
end

The problem is that acts_as_list_no_update is automatically disabled for all classes at the end of each block.

So after all the children have been copied for a parent, and the end of the ::Children.acts_as_list_no_update block, the no_update for the parent is disabled, meaning for each subsequent parent save acts_as_list alters the positions.

(Note that the above snippet is only for illustration, in my situation the parents are in seperate scoped lists).

I believe this issue stems from
https://github.com/swanandp/acts_as_list/blob/248bf23161de8c0fbc1ca05b3b7f8ba59a12f79b/lib/acts_as_list/active_record/acts/no_update.rb#L95

This line clears every class that no_update is enabled for.

Instead of:
extracted_klasses.clear
this line should probably read
extracted_klasses.reject!{|klass| klasses.include?(klass)| }

Hi @conorbdaly, thanks for bring this up. There's a couple of approaches you could take.

  1. I have similar copying functionality in my application and I just let acts_as_list assign new positions for the duplicated items as they're made. As long as you are fetching them in order (I assume you enhance your has_many relationships with an order on the position column? has_many :children, -> { order(:position) }) the duplicated items should be added in order also.
  2. You can use the extra classes functionality of acts_as_list_no_update:
    https://github.com/swanandp/acts_as_list/blob/248bf23161de8c0fbc1ca05b3b7f8ba59a12f79b/lib/acts_as_list/active_record/acts/no_update.rb#L73 You can specify also pausing updates for the Child class like: Parent.acts_as_list_no_update([Child])

Give those a go and let me know how you get on :)

Hi @brendon, thanks for your response.

Yes, we currently have a workaround, by only explicitly wrapping each .save! call instead of wrapping each full loop.

I think that my point is more that its unexpected behavior to close the outer no_update block when an inner block ends. By using the block syntax, I would expect all acts_as_list operations for that class to be disabled until the end of the block, regardless of what other operations happen within the block.

We actually got hit with this as a bug in production, and it can be pretty hard to spot. Most DBMS's will give the results back based on actual DB id, so if the list was never re-ordered, the results will be correct.
Now, I know that one was our fault for not testing properly, but the end result was intermittent and difficult to track down.

I do understand if you guys don't want to change the functionality, but would you consider maybe adding a line or two to the readme.md to explain that nested blocks wont work with no_update?

Thanks @conorbdaly, I think you're right here in that the documentation is kind of hidden away as to how to correctly use this feature: https://github.com/swanandp/acts_as_list/blob/248bf23161de8c0fbc1ca05b3b7f8ba59a12f79b/lib/acts_as_list/active_record/acts/no_update.rb

Perhaps we could forbid nested no_update blocks and warn people to use the additional class method instead, or we could change this so that one could nest no_update blocks.

Would you be keen to look into this?

Threw an hour into this and came up with #343. This should enable nesting of no_update blocks.

Thanks @conorbdaly, I really appreciate the time you put into this. I've merged this and will do a new release shortly.