module ActiveRecord::Acts::List::ClassMethods
This acts_as
extension provides the capabilities for sorting
and reordering a number of objects in a list. The class that has this
specified needs to have a position
column defined as an
integer on the mapped database table.
Todo list example:
class TodoList < ActiveRecord::Base has_many :todo_items, order: "position" end class TodoItem < ActiveRecord::Base belongs_to :todo_list acts_as_list scope: :todo_list end todo_list.first.move_to_bottom todo_list.last.move_higher
Public Instance Methods
Configuration options are:
-
column
- specifies the column name to use for keeping the position integer (default:position
) -
scope
- restricts what is to be considered a list. Given a symbol, it'll attach_id
(if it hasn't already been added) and use that as the foreign key restriction. It's also possible to give it an entire string that is interpolated if you need a tighter scope than just a foreign key. Example:acts_as_list scope: 'todo_list_id = #{todo_list_id} AND completed = 0'
-
top_of_list
- defines the integer used for the top of the list. Defaults to 1. Use 0 to make the collection act more like an array in its indexing. -
add_new_at
- specifies whether objects get added to the :top or :bottom of the list. (default:bottom
)`nil` will result in new items not being added to the list on create
# File lib/acts_as_list/active_record/acts/list.rb, line 37 def acts_as_list(options = {}) configuration = { column: "position", scope: "1 = 1", top_of_list: 1, add_new_at: :bottom} configuration.update(options) if options.is_a?(Hash) configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/ if configuration[:scope].is_a?(Symbol) scope_methods = %Q( def scope_condition self.class.send(:sanitize_sql_hash_for_conditions, { :#{configuration[:scope].to_s} => send(:#{configuration[:scope].to_s}) }) end def scope_changed? changes.include?(scope_name.to_s) end ) elsif configuration[:scope].is_a?(Array) scope_methods = %Q( def attrs %w(#{configuration[:scope].join(" ")}).inject({}) do |memo,column| memo[column.intern] = send(column.intern); memo end end def scope_changed? (attrs.keys & changes.keys.map(&:to_sym)).any? end def scope_condition self.class.send(:sanitize_sql_hash_for_conditions, attrs) end ) else scope_methods = %Q( def scope_condition "#{configuration[:scope]}" end def scope_changed?() false end ) end class_eval <<-EOV include ::ActiveRecord::Acts::List::InstanceMethods def acts_as_list_top #{configuration[:top_of_list]}.to_i end def acts_as_list_class ::#{self.name} end def position_column '#{configuration[:column]}' end def scope_name '#{configuration[:scope]}' end def add_new_at '#{configuration[:add_new_at]}' end #{scope_methods} # only add to attr_accessible # if the class has some mass_assignment_protection if defined?(accessible_attributes) and !accessible_attributes.blank? attr_accessible :#{configuration[:column]} end before_destroy :reload_position after_destroy :decrement_positions_on_lower_items before_update :check_scope after_update :update_positions before_validation :check_top_position scope :in_list, lambda { where("#{table_name}.#{configuration[:column]} IS NOT NULL") } EOV if configuration[:add_new_at].present? self.send(:before_create, "add_to_list_#{configuration[:add_new_at]}") end end