ClassicActsAsList

Description

This is a hard fork off the ActsAsList gem, version 0.5.0- modernized to work with rails 5 and 6. 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.

Installation

In your Gemfile:

gem 'classic_acts_as_list'

Or, from the command line:

gem install classic_acts_as_list

You also need to add the following function to models/application_record.rb for Rails 5+.

  def self.sanitize_sql_hash_for_conditions(attrs)
    self.where(attrs).to_sql.split('WHERE ')[1]
  end

Example

At first, you need to add a position column to desired table:

rails g migration AddPositionToTodoItem position:integer
rake db:migrate

After that you can use acts_as_list method in the model:

class TodoList < ActiveRecord::Base
  has_many :todo_items, -> { order("position ASC") }
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 Methods Added To ActiveRecord Models

You'll have a number of methods added to each instance of the ActiveRecord model that to which acts_as_list is added.

In acts_as_list, "higher" means further up the list (a lower position), and "lower" means further down the list (a higher position). That can be confusing, so it might make sense to add tests that validate that you're using the right method given your context.

Methods That Change Position and Reorder List

  • list_item.insert_at(2)
  • list_item.move_lower will do nothing if the item is the lowest item
  • list_item.move_higher will do nothing if the item is the highest item
  • list_item.move_to_bottom
  • list_item.move_to_top
  • list_item.remove_from_list

Methods That Change Position Without Reordering List

  • list_item.increment_position
  • list_item.decrement_position
  • list_item.set_list_position(3)

Methods That Return Attributes of the Item's List Position

  • list_item.first?
  • list_item.last?
  • list_item.in_list?
  • list_item.not_in_list?
  • list_item.default_position?
  • list_item.higher_item
  • list_item.higher_items will return all the items above list_item in the list (ordered by the position, ascending)
  • list_item.lower_item
  • list_item.lower_items will return all the items below list_item in the list (ordered by the position, ascending)

Notes

If the position column has a default value, then there is a slight change in behavior, i.e if you have 4 items in the list, and you insert 1, with a default position 0, it would be pushed to the bottom of the list. Please look at the tests for this and some recent pull requests for discussions related to this.

All position queries (select, update, etc.) inside gem methods are executed without the default scope (i.e. Model.unscoped), this will prevent nasty issues when the default scope is different from acts_as_list scope.

The position column is set after validations are called, so you should not put a presence validation on the position column.

If you need a scope by a non-association field you should pass an array, containing field name, to a scope:

class TodoItem < ActiveRecord::Base
  # `kind` is a plain text field (e.g. 'work', 'shopping', 'meeting'), not an association
  acts_as_list scope: [:kind]
end

Versions

All versions 0.5.4 onwards were designed to work with Rails 4.2.x and higher.

Roadmap

  • Rails 6 compatibility.

Contributing to classic_acts_as_list

  • Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
  • Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
  • Fork the project
  • Start a feature/bugfix branch
  • Commit and push until you are happy with your contribution
  • Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
  • MAINTAIN BACKWARDS-COMPATIBILITY!!! The whole reason for this fork was that the original maintainers decided to break things that worked.

Copyright (c) 2020 Steven Bong, released under the MIT license