ruby-on-railssortingruby-on-rails-3.1ruby-on-rails-pluginsacts-as-list

Should acts_as_list automatically reorder items when a resource is updated with update_attributes?


I'm using the acts_as_list plugin with rails 3.1 and whenever I add or remove a resource existing positions are ordered and updated fine. However, when updating the position of a resource with update_attributes no reordering seems to take place and I can be left with resources that have duplicated positions. Is this the correct behaviour of acts_as_list?

I've searched for clarification but documentation on this plug in is really limited.

I'm not using javascript at this stage, the position is simply determined with a select box that has an appropriately limited range.

Thanks


Solution

  • There's nothing in the plugin that gets called when the object is updated. You'll have to do it manually using the built-in methods that acts_as_list provides.

    The ruby file for this plugin is only 300 lines and easy to follow, it will tell you everything there is to know about acts_as_list... honestly there's not much to it: https://github.com/swanandp/acts_as_list/blob/master/lib/acts_as_list/active_record/acts/list.rb

    To do it manually, just get all items with a position higher than the current item and increment each one. This is how I handle it in my apps (not using acts_as_list but the concept is there). This is example is for ordering forums:

    forum.rb

    PositionOptions = [["First Forum", "-1"]] + all.map{|forum| ["After #{forum.subject}", forum.position]}
    
    attr_accessor :position_before
      before_save :set_position, :if => "position_before.present?"
        def set_position
          self.position = position_before.to_i + 1
        end
    
      after_save :do_positions, :if => "position_before.present?"
        def do_positions
          Forum.where('position >= ? AND id != ?', position, id).order('position, updated_at DESC').all.each_with_index do |forum, index|
            forum.update_attribute('position', position + index + 1)
          end
        end
    

    view

    <% position_options = Forum::PositionOptions %>
    <%= f.select :position_before, position_options.reject {|forum| @forum.position == forum[1]}, :selected => position_options[position_options.index {|p| p[1] == @forum.position} - 1][1] %>
    

    Maybe not the best way to do it, but it was the best I could come up with. It will probably be too expensive if you plan on the positions being updated often, or if there will be a lot of objects being positioned... but it works for small collections like forums.