[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-8053":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":15,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":24,"hasPages":24,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":29,"discoverSource":30},8053,"acts_as_list","brendon\u002Facts_as_list","brendon","An ActiveRecord plugin for managing lists.","http:\u002F\u002Fbrendon.github.io\u002Facts_as_list\u002F",null,"Ruby",2082,359,16,1,0,2,3,4,29.67,"MIT License",false,"master",true,[],"2026-06-12 02:01:48","# Acts As List\n\n## Build Status\n[![Continuous Integration](https:\u002F\u002Fgithub.com\u002Fbrendon\u002Facts_as_list\u002Factions\u002Fworkflows\u002Fcontinuous_integration.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002Fbrendon\u002Facts_as_list\u002Factions\u002Fworkflows\u002Fcontinuous_integration.yml)\n[![Gem Version](https:\u002F\u002Fbadge.fury.io\u002Frb\u002Facts_as_list.svg)](https:\u002F\u002Fbadge.fury.io\u002Frb\u002Facts_as_list)\n\n## ANNOUNCING: Positioning, the gem\nAs maintainer of both Acts As List and the Ranked Model gems, I've become intimately acquainted with the strengths and weaknesses of each. I ended up writing a small scale Rails Concern for positioning database rows for a recent project and it worked really well so I've decided to release it as a gem: [Positioning](https:\u002F\u002Fgithub.com\u002Fbrendon\u002Fpositioning)\n\nPositioning works similarly to Acts As List in that it maintains a sequential list of integer values as positions. It differs in that it encourages a unique constraints on the position column and supports multiple lists per database table. It borrows Ranked Model's concept of relative positioning. I encourage you to check it out and give it a whirl on your project!\n\n## Description\n\nThis `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.\n\n## 0.8.0 Upgrade Notes\n\nThere are a couple of changes of behaviour from `0.8.0` onwards:\n\n- If you specify `add_new_at: :top`, new items will be added to the top of the list like always. But now, if you specify a position at insert time: `.create(position: 3)`, the position will be respected. In this example, the item will end up at position `3` and will move other items further down the list. Before `0.8.0` the position would be ignored and the item would still be added to the top of the list. [#220](https:\u002F\u002Fgithub.com\u002Fbrendon\u002Facts_as_list\u002Fpull\u002F220)\n- `acts_as_list` now copes with disparate position integers (i.e. gaps between the numbers). There has been a change in behaviour for the `higher_items` method. It now returns items with the first item in the collection being the closest item to the reference item, and the last item in the collection being the furthest from the reference item (a.k.a. the first item in the list). [#223](https:\u002F\u002Fgithub.com\u002Fbrendon\u002Facts_as_list\u002Fpull\u002F223)\n\n## Installation\n\nIn your Gemfile:\n\n    gem 'acts_as_list'\n\nOr, from the command line:\n\n    gem install acts_as_list\n\n## Example\n\nAt first, you need to add a `position` column to desired table:\n\n    rails g migration AddPositionToTodoItem position:integer\n    rake db:migrate\n\nAfter that you can use `acts_as_list` method in the model:\n\n```ruby\nclass TodoList \u003C ActiveRecord::Base\n  has_many :todo_items, -> { order(position: :asc) }\nend\n\nclass TodoItem \u003C ActiveRecord::Base\n  belongs_to :todo_list\n  acts_as_list scope: :todo_list\nend\n\ntodo_list = TodoList.find(...)\ntodo_list.todo_items.first.move_to_bottom\ntodo_list.todo_items.last.move_higher\n```\n\n## Instance Methods Added To ActiveRecord Models\n\nYou'll have a number of methods added to each instance of the ActiveRecord model that to which `acts_as_list` is added.\n\nIn `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.\n\n### Methods That Change Position and Reorder List\n\n- `list_item.insert_at(2)`\n- `list_item.move_lower` will do nothing if the item is the lowest item\n- `list_item.move_higher` will do nothing if the item is the highest item\n- `list_item.move_to_bottom`\n- `list_item.move_to_top`\n- `list_item.remove_from_list`\n\n### Methods That Change Position Without Reordering List\n\n- `list_item.increment_position`\n- `list_item.decrement_position`\n- `list_item.set_list_position(3)`\n\n### Methods That Return Attributes of the Item's List Position\n- `list_item.first?`\n- `list_item.last?`\n- `list_item.in_list?`\n- `list_item.not_in_list?`\n- `list_item.default_position?`\n- `list_item.higher_item`\n- `list_item.higher_items` will return all the items above `list_item` in the list (ordered by the position, ascending)\n- `list_item.lower_item`\n- `list_item.lower_items` will return all the items below `list_item` in the list (ordered by the position, ascending)\n\n## Adding `acts_as_list` To An Existing Model\nAs it stands `acts_as_list` requires position values to be set on the model before the instance methods above will work. Adding something like the below to your migration will set the default position. Change the parameters to order if you want a different initial ordering.\n\n```ruby\nclass AddPositionToTodoItem \u003C ActiveRecord::Migration\n  def change\n    add_column :todo_items, :position, :integer\n    TodoItem.order(:updated_at).each.with_index(1) do |todo_item, index|\n      todo_item.update_column :position, index\n    end\n  end\nend\n```\n\nIf you are using the scope option things can get a bit more complicated. Let's say you have `acts_as_list scope: :todo_list`, you might instead need something like this:\n\n```ruby\nTodoList.all.each do |todo_list|\n  todo_list.todo_items.order(:updated_at).each.with_index(1) do |todo_item, index|\n    todo_item.update_column :position, index\n  end\nend\n```\n\nWhen using PostgreSQL, it is also possible to leave this migration up to the database layer. Inside of the `change` block you could write:\n\n```ruby\n execute \u003C\u003C~SQL.squish\n   UPDATE todo_items\n   SET position = mapping.new_position\n   FROM (\n     SELECT\n       id,\n       ROW_NUMBER() OVER (\n         PARTITION BY todo_list_id\n         ORDER BY updated_at\n       ) AS new_position\n     FROM todo_items\n   ) AS mapping\n   WHERE todo_items.id = mapping.id;\n SQL\n```\n\n## Notes\nAll `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.\n\nThe `position` column is set after validations are called, so you should not put a `presence` validation on the `position` column.\n\n\nIf you need a scope by a non-association field you should pass an array, containing field name, to a scope:\n```ruby\nclass TodoItem \u003C ActiveRecord::Base\n  # `task_category` is a plain text field (e.g. 'work', 'shopping', 'meeting'), not an association\n  acts_as_list scope: [:task_category]\nend\n```\n\nYou can also add multiple scopes in this fashion:\n```ruby\nclass TodoItem \u003C ActiveRecord::Base\n  belongs_to :todo_list\n  acts_as_list scope: [:task_category, :todo_list_id]\nend\n```\n\nFurthermore, you can optionally include a hash of fixed parameters that will be included in all queries:\n```ruby\nclass TodoItem \u003C ActiveRecord::Base\n  belongs_to :todo_list\n  # or `discarded_at` if using discard\n  acts_as_list scope: [:task_category, :todo_list_id, deleted_at: nil]\nend\n```\n\nThis is useful when using this gem in conjunction with the popular [acts_as_paranoid](https:\u002F\u002Fgithub.com\u002FActsAsParanoid\u002Facts_as_paranoid) or [discard](https:\u002F\u002Fgithub.com\u002Fjhawthorn\u002Fdiscard) gems.\n\n## More Options\n- `column`\ndefault: `position`. Use this option if the column name in your database is different from position.\n- `top_of_list`\ndefault: `1`. Use this option to define the top of the list. Use 0 to make the collection act more like an array in its indexing.\n- `add_new_at`\ndefault: `:bottom`. Use this option to specify whether objects get added to the `:top` or `:bottom` of the list. `nil` will result in new items not being added to the list on create, i.e, position will be kept nil after create.\n- `touch_on_update`\ndefault: `true`. Use `touch_on_update: false` if you don't want to update the timestamps of the associated records.\n- `sequential_updates`\nSpecifies whether insert_at should update objects positions during shuffling one by one to respect position column unique not null constraint. Defaults to true if position column has unique index, otherwise false. If constraint is `deferrable initially deferred` (PostgreSQL), overriding it with false will speed up insert_at.\n\n## Disabling temporarily\n\nIf you need to temporarily disable `acts_as_list` during specific operations such as mass-update or imports:\n```ruby\nTodoItem.acts_as_list_no_update do\n  perform_mass_update\nend\n```\nIn an `acts_as_list_no_update` block, all callbacks are disabled, and positions are not updated. New records will be created with\n the default value from the database. It is your responsibility to correctly manage `positions` values.\n\nYou can also pass an array of classes as an argument to disable database updates on just those classes. It can be any ActiveRecord class that has acts_as_list enabled.\n```ruby\nclass TodoList \u003C ActiveRecord::Base\n  has_many :todo_items, -> { order(position: :asc) }\n  acts_as_list\nend\n\nclass TodoItem \u003C ActiveRecord::Base\n  belongs_to :todo_list\n  has_many :todo_attachments, -> { order(position: :asc) }\n\n  acts_as_list scope: :todo_list\nend\n\nclass TodoAttachment \u003C ActiveRecord::Base\n  belongs_to :todo_item\n  acts_as_list scope: :todo_item\nend\n\nTodoItem.acts_as_list_no_update([TodoAttachment]) do\n  TodoItem.find(10).update(position: 2)\n  TodoAttachment.find(10).update(position: 1)\n  TodoAttachment.find(11).update(position: 2)\n  TodoList.find(2).update(position: 3) # For this instance the callbacks will be called because we haven't passed the class as an argument\nend\n```\n\n## Troubleshooting Database Deadlock Errors\nWhen using this gem in an app with a high amount of concurrency, you may see \"deadlock\" errors raised by your database server.\nIt's difficult for the gem to provide a solution that fits every app.\nHere are some steps you can take to mitigate and handle these kinds of errors.\n\n### 1) Use the Most Concise API\nOne easy way to reduce deadlocks is to use the most concise gem API available for what you want to accomplish.\nIn this specific example, the more concise API for creating a list item at a position results in one transaction instead of two,\nand it issues fewer SQL statements. Issuing fewer statements tends to lead to faster transactions.\nFaster transactions are less likely to deadlock.\n\nExample:\n```ruby\n# Good\nTodoItem.create(todo_list: todo_list, position: 1)\n\n# Bad\nitem = TodoItem.create(todo_list: todo_list)\nitem.insert_at(1)\n```\n\n### 2) Rescue then Retry\nDeadlocks are always a possibility when updating tables rows concurrently.\nThe general advice from MySQL documentation is to catch these errors and simply retry the transaction; it will probably succeed on another attempt. (see [How to Minimize and Handle Deadlocks](https:\u002F\u002Fdev.mysql.com\u002Fdoc\u002Frefman\u002F8.0\u002Fen\u002Finnodb-deadlocks-handling.html))\nRetrying transactions sounds simple, but there are many details that need to be chosen on a per-app basis:\nHow many retry attempts should be made?\nShould there be a wait time between attempts?\nWhat _other_ statements were in the transaction that got rolled back?\n\nHere a simple example of rescuing from deadlock and retrying the operation:\n*  `ActiveRecord::Deadlocked` is available in Rails >= 5.1.0.\n* If you have Rails \u003C 5.1.0, you will need to rescue `ActiveRecord::StatementInvalid` and check `#cause`.\n```ruby\n  attempts_left = 2\n  while attempts_left > 0\n    attempts_left -= 1\n    begin\n      TodoItem.transaction do\n        TodoItem.create(todo_list: todo_list, position: 1)\n      end\n      attempts_left = 0\n    rescue ActiveRecord::Deadlocked\n      raise unless attempts_left > 0\n    end\n  end\n```\n\nYou can also use the approach suggested in this StackOverflow post:\nhttps:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F4027659\u002Factiverecord3-deadlock-retry\n\n### 3) Lock Parent Record\nIn addition to reacting to deadlocks, it is possible to reduce their frequency with more pessimistic locking.\nThis approach uses the parent record as a mutex for the entire list.\nThis kind of locking is very effective at reducing the frequency of deadlocks while updating list items.\nHowever, there are some things to keep in mind:\n* This locking pattern needs to be used around *every* call that modifies the list; even if it does not reorder list items.\n* This locking pattern effectively serializes operations on the list. The throughput of operations on the list will decrease.\n* Locking the parent record may lead to deadlock elsewhere if some other code also locks the parent table.\n\nExample:\n```ruby\ntodo_list = TodoList.create(name: \"The List\")\ntodo_list.with_lock do\n  item = TodoItem.create(description: \"Buy Groceries\", todo_list: todo_list, position: 1)\nend\n```\n\n## Versions\nVersion `0.9.0` adds `acts_as_list_no_update` (https:\u002F\u002Fgithub.com\u002Fbrendon\u002Facts_as_list\u002Fpull\u002F244) and compatibility with not-null and uniqueness constraints on the database (https:\u002F\u002Fgithub.com\u002Fbrendon\u002Facts_as_list\u002Fpull\u002F246). These additions shouldn't break compatibility with existing implementations.\n\nAs of version `0.7.5` Rails 5 is supported.\n\nAll versions `0.1.5` onwards require Rails 3.0.x and higher.\n\n## A note about data integrity\n\nWe often hear complaints that `position` values are repeated, incorrect etc. For example, #254. To ensure data integrity, you should rely on your database. There are two things you can do:\n\n1. Use constraints.  If you model `Item` that `belongs_to` an `Order`, and it has a `position` column, then add a unique constraint on `items` with `[:order_id, :position]`.  Think of it as a list invariant. What are the properties of your list that don't change no matter how many items you have in it?  One such property is that each item has a distinct position. Another _could be_ that position is always greater than 0. It is strongly recommended that you rely on your database to enforce these invariants or constraints. Here are the docs for [PostgreSQL](https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002F9.5\u002Fstatic\u002Fddl-constraints.html) and [MySQL](https:\u002F\u002Fdev.mysql.com\u002Fdoc\u002Frefman\u002F8.0\u002Fen\u002Falter-table.html).\n2. Use mutexes or row level locks. At its heart the duplicate problem is that of handling concurrency. Adding a contention resolution mechanism like locks will solve it to some extent.  But it is not a solution or replacement for constraints.  Locks are also prone to deadlocks.\n\nAs a library, `acts_as_list` may not always have all the context needed to apply these tools. They are much better suited at the application level.\n\n\n## Roadmap\n\n1. Sort based feature\n\n## Contributing to `acts_as_list`\n\n- Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet\n- Check out the issue tracker to make sure someone already hasn't requested it and\u002For contributed it\n- Fork the project\n- Start a feature\u002Fbugfix branch\n- Commit and push until you are happy with your contribution\n- Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.\n- 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.\n- I would recommend using Rails 3.1.x and higher for testing the build before a pull request. The current test harness does not quite work with 3.0.x. The plugin itself works, but the issue lies with testing infrastructure.\n\n## Copyright\n\nCopyright (c) 2007 David Heinemeier Hansson, released under the MIT license\n","acts_as_list 是一个 ActiveRecord 插件，用于管理和排序列表中的对象。它通过在数据库表中定义一个整数类型的 `position` 列来实现对象的顺序排列和重新排序功能。项目支持在插入新记录时指定位置，并能处理列表中位置数字不连续的情况。此外，该插件还允许开发者自定义列表添加新项的位置（顶部或底部）。适用于需要对关联数据进行有序管理的应用场景，如待办事项列表、文章分类等。使用 Ruby 语言开发，遵循 MIT 许可协议，易于集成到现有的 Rails 项目中。","2026-06-11 03:15:50","top_language"]