diff --git a/CHANGELOG.md b/CHANGELOG.md index 65ab6ad..56ffc3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [Unreleased] +## [0.4.3] - 2024-11-18 + +- Add support for polymorphic `belongs_to` where we add both the `id` and the `type` to the scope. + ## [0.4.2] - 2024-11-08 NOTE: Versions 0.4.0 and 0.4.1 contain fatal flaws with the locking logic. Upgrade as soon as you can. diff --git a/README.md b/README.md index eb38f26..42de751 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,10 @@ You should also add an index to ensure that the `position` column value is uniqu The above assumes that your items are scoped to a parent table called `lists`. +If you have a polymorphic `belongs_to` then you'll want to add the type column to the index also: + +`add_index :items, [:listable_id, :listable_type, :position], unique: true` + The Positioning gem uses `0` and negative integers to rearrange the lists it manages so don't add database validations to restrict the usage of these. You are also restricted from using `0` and negative integers as position values. If you try, the position value will become `1`. If you try to set an explicit position value that is greater than the next available list position, it will be rounded down to that value. ### Declaring Positioning @@ -66,6 +70,10 @@ positioned on: :type belongs_to :list belongs_to :category positioned on: [:list, :category, :enabled] + +# If your belongs_to is polymorphic positioning will automatically add the type to the scope +belongs_to :listable, polymorphic: true +positioned on: :listable ``` ### Initialising a List diff --git a/lib/positioning.rb b/lib/positioning.rb index 023343e..638de7d 100644 --- a/lib/positioning.rb +++ b/lib/positioning.rb @@ -36,6 +36,7 @@ def positioned(on: [], column: :position) if reflection&.belongs_to? positioning_columns[column][:scope_columns] << reflection.foreign_key + positioning_columns[column][:scope_columns] << reflection.foreign_type if reflection.polymorphic? positioning_columns[column][:scope_associations] << reflection.name else positioning_columns[column][:scope_columns] << scope_component diff --git a/test/models/entity.rb b/test/models/entity.rb new file mode 100644 index 0000000..977081c --- /dev/null +++ b/test/models/entity.rb @@ -0,0 +1,5 @@ +class Entity < ActiveRecord::Base + belongs_to :includable, polymorphic: true + + positioned on: :includable +end diff --git a/test/support/active_record.rb b/test/support/active_record.rb index b9f2b8a..381b412 100644 --- a/test/support/active_record.rb +++ b/test/support/active_record.rb @@ -13,6 +13,14 @@ t.string :name end + create_table :entities, force: true do |t| + t.string :name + t.integer :position, null: false + t.references :includable, polymorphic: true + end + + add_index :entities, [:includable_id, :includable_type, :position], unique: true + create_table :items, force: true do |t| t.string :name t.integer :position, null: false diff --git a/test/test_positioning.rb b/test/test_positioning.rb index 8542940..4b99b38 100644 --- a/test/test_positioning.rb +++ b/test/test_positioning.rb @@ -11,6 +11,7 @@ require_relative "models/author/teacher" require_relative "models/blog" require_relative "models/post" +require_relative "models/entity" class TestRelativePositionStruct < Minitest::Test def test_struct_takes_keyword_arguments @@ -614,6 +615,10 @@ def test_that_position_columns_will_cope_with_standard_columns assert_equal({position: {scope_columns: ["list_id", "enabled"], scope_associations: [:list]}}, Author.positioning_columns) end + def test_that_position_columns_will_cope_with_polymorphic_belong_to + assert_equal({position: {scope_columns: ["includable_id", "includable_type"], scope_associations: [:includable]}}, Entity.positioning_columns) + end + def test_that_position_columns_must_have_unique_keys assert_raises(Positioning::Error) do Item.send :positioned, on: :list