Module: ActiveRecord::Acts::List::ClassMethods

Defined in:
lib/acts_as_list/active_record/acts/list.rb

Overview

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

Instance Method Summary collapse

Instance Method Details

#acts_as_list(options = {}) ⇒ Object

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


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# 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 = %(
      def scope_condition
        { :#{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 = %(
      def attrs
        %w(#{configuration[:scope].join(" ")}).inject({}) do |memo,column|
          memo[column.intern] = read_attribute(column.intern); memo
        end
      end

      def scope_changed?
        (attrs.keys & changes.keys.map(&:to_sym)).any?
      end

      def scope_condition
        attrs
      end
    )
  else
    scope_methods = %(
      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

    def #{configuration[:column]}=(position)
      write_attribute(:#{configuration[:column]}, position)
      @position_changed = true
    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