diff --git a/lib/closure_tree/has_closure_tree.rb b/lib/closure_tree/has_closure_tree.rb index b0bc5b1..4b19f78 100644 --- a/lib/closure_tree/has_closure_tree.rb +++ b/lib/closure_tree/has_closure_tree.rb @@ -11,7 +11,8 @@ def has_closure_tree(options = {}) :dont_order_roots, :numeric_order, :touch, - :with_advisory_lock + :with_advisory_lock, + :order_belong_to ) class_attribute :_ct diff --git a/lib/closure_tree/model.rb b/lib/closure_tree/model.rb index a3de39a..2ecb5be 100644 --- a/lib/closure_tree/model.rb +++ b/lib/closure_tree/model.rb @@ -171,6 +171,10 @@ def _ct_parent_id read_attribute(_ct.parent_column_sym) end + def _ct_belong_to_id + read_attribute(_ct.belong_to_column_sym) + end + def _ct_quoted_parent_id _ct.quoted_value(_ct_parent_id) end diff --git a/lib/closure_tree/numeric_deterministic_ordering.rb b/lib/closure_tree/numeric_deterministic_ordering.rb index 4afe578..535b5dd 100644 --- a/lib/closure_tree/numeric_deterministic_ordering.rb +++ b/lib/closure_tree/numeric_deterministic_ordering.rb @@ -12,17 +12,17 @@ module NumericDeterministicOrdering def _ct_reorder_prior_siblings_if_parent_changed if public_send(:saved_change_to_attribute?, _ct.parent_column_name) && !@was_new_record was_parent_id = public_send(:attribute_before_last_save, _ct.parent_column_name) - _ct.reorder_with_parent_id(was_parent_id) + _ct.reorder_with_parent_id(parent_id: was_parent_id, belong_to_name: _ct.belong_to_column_sym, belong_to_id: _ct_belong_to_id) end end def _ct_reorder_siblings(minimum_sort_order_value = nil) - _ct.reorder_with_parent_id(_ct_parent_id, minimum_sort_order_value) + _ct.reorder_with_parent_id(parent_id: _ct_parent_id, minimum_sort_order_value: minimum_sort_order_value, belong_to_name: _ct.belong_to_column_sym, belong_to_id: _ct_belong_to_id) reload unless destroyed? end def _ct_reorder_children(minimum_sort_order_value = nil) - _ct.reorder_with_parent_id(_ct_id, minimum_sort_order_value) + _ct.reorder_with_parent_id(parent_id: _ct_id, minimum_sort_order_value: minimum_sort_order_value) end def self_and_descendants_preordered diff --git a/lib/closure_tree/numeric_order_support.rb b/lib/closure_tree/numeric_order_support.rb index 0223a41..a867216 100644 --- a/lib/closure_tree/numeric_order_support.rb +++ b/lib/closure_tree/numeric_order_support.rb @@ -13,38 +13,48 @@ def self.adapter_for_connection(connection) end module MysqlAdapter - def reorder_with_parent_id(parent_id, minimum_sort_order_value = nil) + def reorder_with_parent_id(parent_id:, minimum_sort_order_value: nil, belong_to_name: nil, belong_to_id: nil) return if parent_id.nil? && dont_order_roots min_where = if minimum_sort_order_value "AND #{quoted_order_column} >= #{minimum_sort_order_value}" else "" end + belong_to_scope = if parent_id.nil? && belong_to_id + "AND #{quoted_table_name}.#{belong_to_name} = #{belong_to_id}" + else + "" + end connection.execute 'SET @i = 0' connection.execute <<-SQL.squish UPDATE #{quoted_table_name} SET #{quoted_order_column} = (@i := @i + 1) + #{minimum_sort_order_value.to_i - 1} - WHERE #{where_eq(parent_column_name, parent_id)} #{min_where} + WHERE #{where_eq(parent_column_name, parent_id)} #{min_where} #{belong_to_scope} ORDER BY #{nulls_last_order_by} SQL end end module PostgreSQLAdapter - def reorder_with_parent_id(parent_id, minimum_sort_order_value = nil) + def reorder_with_parent_id(parent_id:, minimum_sort_order_value: nil, belong_to_name: nil, belong_to_id: nil) return if parent_id.nil? && dont_order_roots min_where = if minimum_sort_order_value "AND #{quoted_order_column} >= #{minimum_sort_order_value}" else "" end + belong_to_scope = if parent_id.nil? && belong_to_id + "AND #{quoted_table_name}.#{belong_to_name} = #{belong_to_id}" + else + "" + end connection.execute <<-SQL.squish UPDATE #{quoted_table_name} SET #{quoted_order_column(false)} = t.seq + #{minimum_sort_order_value.to_i - 1} FROM ( SELECT #{quoted_id_column_name} AS id, row_number() OVER(ORDER BY #{order_by}) AS seq FROM #{quoted_table_name} - WHERE #{where_eq(parent_column_name, parent_id)} #{min_where} + WHERE #{where_eq(parent_column_name, parent_id)} #{min_where} #{belong_to_scope} ) AS t WHERE #{quoted_table_name}.#{quoted_id_column_name} = t.id and #{quoted_table_name}.#{quoted_order_column(false)} is distinct from t.seq + #{minimum_sort_order_value.to_i - 1} @@ -57,7 +67,7 @@ def rows_updated(result) end module GenericAdapter - def reorder_with_parent_id(parent_id, minimum_sort_order_value = nil) + def reorder_with_parent_id(parent_id:, minimum_sort_order_value: nil, belong_to_name: nil, belong_to_id: nil) return if parent_id.nil? && dont_order_roots scope = model_class. where(parent_column_sym => parent_id). @@ -65,6 +75,7 @@ def reorder_with_parent_id(parent_id, minimum_sort_order_value = nil) if minimum_sort_order_value scope = scope.where("#{quoted_order_column} >= #{minimum_sort_order_value}") end + scope = scope.where(belong_to_name => belong_to_id) if belong_to_id scope.each_with_index do |ea, idx| ea.update_order_value(idx + minimum_sort_order_value.to_i) end diff --git a/lib/closure_tree/support_attributes.rb b/lib/closure_tree/support_attributes.rb index 38879ad..2741c29 100644 --- a/lib/closure_tree/support_attributes.rb +++ b/lib/closure_tree/support_attributes.rb @@ -36,6 +36,14 @@ def parent_column_sym parent_column_name.to_sym end + def belong_to_column_name + options[:order_belong_to] + end + + def belong_to_column_sym + belong_to_column_name&.to_sym + end + def name_column options[:name_column] end diff --git a/spec/closure_tree/root_ordering.rb b/spec/closure_tree/root_ordering.rb new file mode 100644 index 0000000..e2c8222 --- /dev/null +++ b/spec/closure_tree/root_ordering.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Block do + describe "correct root order_value" do + let!(:group) { Group.create!(name: "TheGroup") } + let!(:user1) { User.create!(email: "1@example.com", group_id: group.id) } + let!(:user2) { User.create!(email: "2@example.com", group_id: group.id) } + let!(:block1) { Block.create!(name: "1block", user_id: user1.id) } + let!(:block2) { Block.create!(name: "2block", user_id: user2.id) } + let!(:block3) { Block.create!(name: "3block", user_id: user1.id) } + let!(:block4) { Block.create!(name: "4block", user_id: user2.id) } + let!(:block5) { Block.create!(name: "5block", user_id: user1.id) } + let!(:block6) { Block.create!(name: "6block", user_id: user2.id) } + + it "should set order_value on roots" do + assert_equal block1.self_and_siblings.pluck(:sort_order), [1,2,3] + assert_equal block2.self_and_siblings.pluck(:sort_order), [1,2,3] + end + end +end diff --git a/spec/support/models.rb b/spec/support/models.rb index a49e013..9a60419 100644 --- a/spec/support/models.rb +++ b/spec/support/models.rb @@ -91,6 +91,13 @@ class ContractType < ApplicationRecord has_many :contracts, inverse_of: :contract_type end +class Block < ApplicationRecord + acts_as_tree order: :column_whereby_ordering_is_inferred, # <- symbol, and not "sort_order" + numeric_order: true, + dependent: :destroy, + order_belong_to: :user_id +end + class Label < ApplicationRecord # make sure order doesn't matter acts_as_tree order: :column_whereby_ordering_is_inferred, # <- symbol, and not "sort_order" diff --git a/spec/support/schema.rb b/spec/support/schema.rb index cca75c5..2efc8de 100644 --- a/spec/support/schema.rb +++ b/spec/support/schema.rb @@ -9,6 +9,14 @@ t.timestamps null: false end + create_table 'blocks' do |t| + t.string 'name' + t.references 'parent' + t.references 'user', null: false + t.integer 'sort_order' + t.timestamps null: false + end + create_table 'tag_hierarchies', id: false do |t| t.references 'ancestor', null: false t.references 'descendant', null: false