module ActiveRecord::Acts::List::InstanceMethods
All the methods available to a record that has had
acts_as_list
specified. Each method works by assuming the
object to be the item in the list, so chapter.move_lower
would
move that chapter lower in the list of all chapters. Likewise,
chapter.first?
would return true
if that chapter
is the first in the list of all chapters.
Public Instance Methods
Decrease the position of this item without adjusting the rest of the list.
# File lib/acts_as_list/active_record/acts/list.rb, line 204 def decrement_position return unless in_list? set_list_position(self.send(position_column).to_i - 1) end
# File lib/acts_as_list/active_record/acts/list.rb, line 272 def default_position acts_as_list_class.columns_hash[position_column.to_s].default end
# File lib/acts_as_list/active_record/acts/list.rb, line 276 def default_position? default_position && default_position.to_i == send(position_column) end
Return true
if this object is the first in the list.
# File lib/acts_as_list/active_record/acts/list.rb, line 210 def first? return false unless in_list? self.send(position_column) == acts_as_list_top end
Return the next higher item in the list.
# File lib/acts_as_list/active_record/acts/list.rb, line 222 def higher_item return nil unless in_list? acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where("#{position_column} < #{(send(position_column).to_i).to_s}"). order("#{acts_as_list_class.table_name}.#{position_column} DESC").first end end
Return the next n higher items in the list selects all higher items by default
# File lib/acts_as_list/active_record/acts/list.rb, line 232 def higher_items(limit=nil) limit ||= acts_as_list_list.count position_value = send(position_column) acts_as_list_list. where("#{position_column} < ?", position_value). where("#{position_column} >= ?", position_value - limit). limit(limit). order("#{acts_as_list_class.table_name}.#{position_column} ASC") end
Test if this record is in a list
# File lib/acts_as_list/active_record/acts/list.rb, line 264 def in_list? !not_in_list? end
Increase the position of this item without adjusting the rest of the list.
# File lib/acts_as_list/active_record/acts/list.rb, line 198 def increment_position return unless in_list? set_list_position(self.send(position_column).to_i + 1) end
Insert the item at the given position (defaults to the top position of 1).
# File lib/acts_as_list/active_record/acts/list.rb, line 138 def insert_at(position = acts_as_list_top) insert_at_position(position) end
Return true
if this object is the last in the list.
# File lib/acts_as_list/active_record/acts/list.rb, line 216 def last? return false unless in_list? self.send(position_column) == bottom_position_in_list end
Return the next lower item in the list.
# File lib/acts_as_list/active_record/acts/list.rb, line 243 def lower_item return nil unless in_list? acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where("#{position_column} > #{(send(position_column).to_i).to_s}"). order("#{acts_as_list_class.table_name}.#{position_column} ASC").first end end
Return the next n lower items in the list selects all lower items by default
# File lib/acts_as_list/active_record/acts/list.rb, line 253 def lower_items(limit=nil) limit ||= acts_as_list_list.count position_value = send(position_column) acts_as_list_list. where("#{position_column} > ?", position_value). where("#{position_column} <= ?", position_value + limit). limit(limit). order("#{acts_as_list_class.table_name}.#{position_column} ASC") end
Swap positions with the next higher item, if one exists.
# File lib/acts_as_list/active_record/acts/list.rb, line 153 def move_higher return unless higher_item acts_as_list_class.transaction do higher_item.increment_position decrement_position end end
Swap positions with the next lower item, if one exists.
# File lib/acts_as_list/active_record/acts/list.rb, line 143 def move_lower return unless lower_item acts_as_list_class.transaction do lower_item.decrement_position increment_position end end
Move to the bottom of the list. If the item is already in the list, the items below it have their position adjusted accordingly.
# File lib/acts_as_list/active_record/acts/list.rb, line 164 def move_to_bottom return unless in_list? acts_as_list_class.transaction do decrement_positions_on_lower_items assume_bottom_position end end
Move to the top of the list. If the item is already in the list, the items above it have their position adjusted accordingly.
# File lib/acts_as_list/active_record/acts/list.rb, line 174 def move_to_top return unless in_list? acts_as_list_class.transaction do increment_positions_on_higher_items assume_top_position end end
Move the item within scope. If a position within the new scope isn't supplied, the item will be appended to the end of the list.
# File lib/acts_as_list/active_record/acts/list.rb, line 192 def move_within_scope(scope_id) send("#{scope_name}=", scope_id) save! end
# File lib/acts_as_list/active_record/acts/list.rb, line 268 def not_in_list? send(position_column).nil? end
Removes the item from the list.
# File lib/acts_as_list/active_record/acts/list.rb, line 183 def remove_from_list if in_list? decrement_positions_on_lower_items set_list_position(nil) end end
Sets the new position and saves it
# File lib/acts_as_list/active_record/acts/list.rb, line 281 def set_list_position(new_position) write_attribute position_column, new_position save(validate: false) end
Private Instance Methods
# File lib/acts_as_list/active_record/acts/list.rb, line 287 def acts_as_list_list acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition) end end
# File lib/acts_as_list/active_record/acts/list.rb, line 298 def add_to_list_bottom if not_in_list? || scope_changed? && !@position_changed || default_position? self[position_column] = bottom_position_in_list.to_i + 1 else increment_positions_on_lower_items(self[position_column], id) end end
# File lib/acts_as_list/active_record/acts/list.rb, line 293 def add_to_list_top increment_positions_on_all_items self[position_column] = acts_as_list_top end
Forces item to assume the bottom position in the list.
# File lib/acts_as_list/active_record/acts/list.rb, line 326 def assume_bottom_position set_list_position(bottom_position_in_list(self).to_i + 1) end
Forces item to assume the top position in the list.
# File lib/acts_as_list/active_record/acts/list.rb, line 331 def assume_top_position set_list_position(acts_as_list_top) end
Returns the bottom item
# File lib/acts_as_list/active_record/acts/list.rb, line 317 def bottom_item(except = nil) conditions = scope_condition conditions = except ? "#{self.class.primary_key} != #{self.class.connection.quote(except.id)}" : {} acts_as_list_class.unscoped do acts_as_list_class.in_list.where(scope_condition).where(conditions).order("#{acts_as_list_class.table_name}.#{position_column} DESC").first end end
Returns the bottom position number in the list.
bottom_position_in_list # => 2
# File lib/acts_as_list/active_record/acts/list.rb, line 311 def bottom_position_in_list(except = nil) item = bottom_item(except) item ? item.send(position_column) : acts_as_list_top - 1 end
# File lib/acts_as_list/active_record/acts/list.rb, line 468 def check_scope if scope_changed? swap_changed_attributes send('decrement_positions_on_lower_items') if lower_item swap_changed_attributes send("add_to_list_#{add_new_at}") end end
This check is skipped if the position is currently the default position from the table as modifying the default position on creation is handled elsewhere
# File lib/acts_as_list/active_record/acts/list.rb, line 483 def check_top_position if send(position_column) && !default_position? && send(position_column) < acts_as_list_top self[position_column] = acts_as_list_top end end
This has the effect of moving all the higher items up one.
# File lib/acts_as_list/active_record/acts/list.rb, line 336 def decrement_positions_on_higher_items(position) acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} <= #{position}" ).update_all( "#{position_column} = (#{position_column} - 1)" ) end end
This has the effect of moving all the lower items up one.
# File lib/acts_as_list/active_record/acts/list.rb, line 347 def decrement_positions_on_lower_items(position=nil) return unless in_list? position ||= send(position_column).to_i acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} > #{position}" ).update_all( "#{position_column} = (#{position_column} - 1)" ) end end
Increments position (position_column
) of all items in the
list.
# File lib/acts_as_list/active_record/acts/list.rb, line 385 def increment_positions_on_all_items acts_as_list_class.unscoped do acts_as_list_class.where( scope_condition ).update_all( "#{position_column} = (#{position_column} + 1)" ) end end
This has the effect of moving all the higher items down one.
# File lib/acts_as_list/active_record/acts/list.rb, line 360 def increment_positions_on_higher_items return unless in_list? acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} < #{send(position_column).to_i}" ).update_all( "#{position_column} = (#{position_column} + 1)" ) end end
This has the effect of moving all the lower items down one.
# File lib/acts_as_list/active_record/acts/list.rb, line 372 def increment_positions_on_lower_items(position, avoid_id = nil) avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : '' acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} >= #{position}#{avoid_id_condition}" ).update_all( "#{position_column} = (#{position_column} + 1)" ) end end
# File lib/acts_as_list/active_record/acts/list.rb, line 431 def insert_at_position(position) return set_list_position(position) if new_record? if in_list? old_position = send(position_column).to_i return if position == old_position shuffle_positions_on_intermediate_items(old_position, position) else increment_positions_on_lower_items(position) end set_list_position(position) end
# File lib/acts_as_list/active_record/acts/list.rb, line 477 def reload_position self.reload end
Overwrite this method to define the scope of the list changes
# File lib/acts_as_list/active_record/acts/list.rb, line 307 def scope_condition() {} end
Reorders intermediate items to support moving an item from old_position to new_position.
# File lib/acts_as_list/active_record/acts/list.rb, line 396 def shuffle_positions_on_intermediate_items(old_position, new_position, avoid_id = nil) return if old_position == new_position avoid_id_condition = avoid_id ? " AND #{self.class.primary_key} != #{self.class.connection.quote(avoid_id)}" : '' if old_position < new_position # Decrement position of intermediate items # # e.g., if moving an item from 2 to 5, # move [3, 4, 5] to [2, 3, 4] acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} > #{old_position}" ).where( "#{position_column} <= #{new_position}#{avoid_id_condition}" ).update_all( "#{position_column} = (#{position_column} - 1)" ) end else # Increment position of intermediate items # # e.g., if moving an item from 5 to 2, # move [2, 3, 4] to [3, 4, 5] acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where( "#{position_column} >= #{new_position}" ).where( "#{position_column} < #{old_position}#{avoid_id_condition}" ).update_all( "#{position_column} = (#{position_column} + 1)" ) end end end
used by #insert_at_position instead of #remove_from_list, as postgresql raises error if position_column has non-null constraint
# File lib/acts_as_list/active_record/acts/list.rb, line 444 def store_at_0 if in_list? old_position = send(position_column).to_i set_list_position(0) decrement_positions_on_lower_items(old_position) end end
Temporarily swap changes attributes with current attributes
# File lib/acts_as_list/active_record/acts/list.rb, line 463 def swap_changed_attributes @changed_attributes.each { |k, _| @changed_attributes[k], self[k] = self[k], @changed_attributes[k] } end
# File lib/acts_as_list/active_record/acts/list.rb, line 452 def update_positions old_position = send("#{position_column}_was").to_i new_position = send(position_column).to_i return unless acts_as_list_class.unscoped do acts_as_list_class.where(scope_condition).where("#{position_column} = #{new_position}").count > 1 end shuffle_positions_on_intermediate_items old_position, new_position, id end