acts_as_ordered_tree 1.3.1 → 2.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/acts_as_ordered_tree.rb +22 -100
- data/lib/acts_as_ordered_tree/adapters.rb +17 -0
- data/lib/acts_as_ordered_tree/adapters/abstract.rb +23 -0
- data/lib/acts_as_ordered_tree/adapters/postgresql.rb +150 -0
- data/lib/acts_as_ordered_tree/adapters/recursive.rb +157 -0
- data/lib/acts_as_ordered_tree/compatibility.rb +22 -0
- data/lib/acts_as_ordered_tree/compatibility/active_record/association_scope.rb +9 -0
- data/lib/acts_as_ordered_tree/compatibility/active_record/default_scoped.rb +19 -0
- data/lib/acts_as_ordered_tree/compatibility/active_record/null_relation.rb +71 -0
- data/lib/acts_as_ordered_tree/compatibility/features.rb +153 -0
- data/lib/acts_as_ordered_tree/deprecate.rb +24 -0
- data/lib/acts_as_ordered_tree/hooks.rb +38 -0
- data/lib/acts_as_ordered_tree/hooks/update.rb +86 -0
- data/lib/acts_as_ordered_tree/instance_methods.rb +92 -453
- data/lib/acts_as_ordered_tree/iterators/arranger.rb +35 -0
- data/lib/acts_as_ordered_tree/iterators/level_calculator.rb +52 -0
- data/lib/acts_as_ordered_tree/iterators/orphans_pruner.rb +58 -0
- data/lib/acts_as_ordered_tree/node.rb +78 -0
- data/lib/acts_as_ordered_tree/node/attributes.rb +48 -0
- data/lib/acts_as_ordered_tree/node/movement.rb +62 -0
- data/lib/acts_as_ordered_tree/node/movements.rb +111 -0
- data/lib/acts_as_ordered_tree/node/predicates.rb +98 -0
- data/lib/acts_as_ordered_tree/node/reloading.rb +49 -0
- data/lib/acts_as_ordered_tree/node/siblings.rb +139 -0
- data/lib/acts_as_ordered_tree/node/traversals.rb +53 -0
- data/lib/acts_as_ordered_tree/persevering_transaction.rb +93 -0
- data/lib/acts_as_ordered_tree/position.rb +143 -0
- data/lib/acts_as_ordered_tree/relation/arrangeable.rb +33 -0
- data/lib/acts_as_ordered_tree/relation/iterable.rb +41 -0
- data/lib/acts_as_ordered_tree/relation/preloaded.rb +46 -11
- data/lib/acts_as_ordered_tree/transaction/base.rb +57 -0
- data/lib/acts_as_ordered_tree/transaction/callbacks.rb +67 -0
- data/lib/acts_as_ordered_tree/transaction/create.rb +68 -0
- data/lib/acts_as_ordered_tree/transaction/destroy.rb +34 -0
- data/lib/acts_as_ordered_tree/transaction/dsl.rb +214 -0
- data/lib/acts_as_ordered_tree/transaction/factory.rb +67 -0
- data/lib/acts_as_ordered_tree/transaction/move.rb +70 -0
- data/lib/acts_as_ordered_tree/transaction/passthrough.rb +12 -0
- data/lib/acts_as_ordered_tree/transaction/reorder.rb +42 -0
- data/lib/acts_as_ordered_tree/transaction/save.rb +64 -0
- data/lib/acts_as_ordered_tree/transaction/update.rb +78 -0
- data/lib/acts_as_ordered_tree/tree.rb +148 -0
- data/lib/acts_as_ordered_tree/tree/association.rb +20 -0
- data/lib/acts_as_ordered_tree/tree/callbacks.rb +57 -0
- data/lib/acts_as_ordered_tree/tree/children_association.rb +120 -0
- data/lib/acts_as_ordered_tree/tree/columns.rb +102 -0
- data/lib/acts_as_ordered_tree/tree/deprecated_columns_accessors.rb +24 -0
- data/lib/acts_as_ordered_tree/tree/parent_association.rb +31 -0
- data/lib/acts_as_ordered_tree/tree/perseverance.rb +19 -0
- data/lib/acts_as_ordered_tree/tree/scopes.rb +56 -0
- data/lib/acts_as_ordered_tree/validators.rb +1 -1
- data/lib/acts_as_ordered_tree/version.rb +1 -1
- data/spec/acts_as_ordered_tree_spec.rb +80 -909
- data/spec/adapters/postgresql_spec.rb +14 -0
- data/spec/adapters/recursive_spec.rb +12 -0
- data/spec/adapters/shared.rb +272 -0
- data/spec/callbacks_spec.rb +177 -0
- data/spec/counter_cache_spec.rb +31 -0
- data/spec/create_spec.rb +110 -0
- data/spec/destroy_spec.rb +57 -0
- data/spec/inheritance_spec.rb +176 -0
- data/spec/move_spec.rb +94 -0
- data/spec/node/movements/concurrent_movements_spec.rb +354 -0
- data/spec/node/movements/move_higher_spec.rb +46 -0
- data/spec/node/movements/move_lower_spec.rb +46 -0
- data/spec/node/movements/move_to_child_of_spec.rb +147 -0
- data/spec/node/movements/move_to_child_with_index_spec.rb +124 -0
- data/spec/node/movements/move_to_child_with_position_spec.rb +85 -0
- data/spec/node/movements/move_to_left_of_spec.rb +120 -0
- data/spec/node/movements/move_to_right_of_spec.rb +120 -0
- data/spec/node/movements/move_to_root_spec.rb +67 -0
- data/spec/node/predicates_spec.rb +211 -0
- data/spec/node/reloading_spec.rb +42 -0
- data/spec/node/siblings_spec.rb +193 -0
- data/spec/node/traversals_spec.rb +71 -0
- data/spec/persevering_transaction_spec.rb +98 -0
- data/spec/relation/arrangeable_spec.rb +88 -0
- data/spec/relation/iterable_spec.rb +104 -0
- data/spec/relation/preloaded_spec.rb +57 -0
- data/spec/reorder_spec.rb +83 -0
- data/spec/spec_helper.rb +30 -38
- data/spec/support/db/boot.rb +22 -0
- data/spec/{db → support/db}/config.travis.yml +2 -0
- data/spec/{db → support/db}/config.yml +1 -0
- data/spec/{db → support/db}/schema.rb +9 -0
- data/spec/support/factories.rb +2 -2
- data/spec/support/matchers.rb +67 -58
- data/spec/support/models.rb +6 -14
- data/spec/support/tree_factory.rb +315 -0
- data/spec/tree/children_association_spec.rb +72 -0
- data/spec/tree/columns_spec.rb +65 -0
- data/spec/tree/scopes_spec.rb +39 -0
- metadata +161 -43
- data/lib/acts_as_ordered_tree/adapters/postgresql_adapter.rb +0 -104
- data/lib/acts_as_ordered_tree/arrangeable.rb +0 -80
- data/lib/acts_as_ordered_tree/class_methods.rb +0 -72
- data/lib/acts_as_ordered_tree/relation/base.rb +0 -26
- data/lib/acts_as_ordered_tree/tenacious_transaction.rb +0 -30
- data/spec/concurrency_support_spec.rb +0 -156
@@ -1,104 +0,0 @@
|
|
1
|
-
require 'acts_as_ordered_tree/arrangeable'
|
2
|
-
require 'acts_as_ordered_tree/relation/preloaded'
|
3
|
-
|
4
|
-
module ActsAsOrderedTree
|
5
|
-
module Adapters
|
6
|
-
module PostgreSQLAdapter
|
7
|
-
# Recursive ancestors fetcher
|
8
|
-
def self_and_ancestors
|
9
|
-
if persisted? && !send("#{parent_column}_changed?")
|
10
|
-
query = <<-QUERY
|
11
|
-
SELECT id, #{parent_column}, 1 AS _depth
|
12
|
-
FROM #{self.class.quoted_table_name}
|
13
|
-
WHERE #{arel[:id].eq(id).to_sql}
|
14
|
-
UNION ALL
|
15
|
-
SELECT alias1.id, alias1.#{parent_column}, _depth + 1
|
16
|
-
FROM #{self.class.quoted_table_name} alias1
|
17
|
-
INNER JOIN self_and_ancestors ON alias1.id = self_and_ancestors.#{parent_column}
|
18
|
-
QUERY
|
19
|
-
|
20
|
-
with_recursive_join(query, 'self_and_ancestors').
|
21
|
-
order('self_and_ancestors._depth DESC').
|
22
|
-
extending(Arrangeable)
|
23
|
-
else
|
24
|
-
(ancestors + [self]).tap { |ary| ary.extend(Arrangeable) }
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# Recursive ancestors fetcher
|
29
|
-
def ancestors
|
30
|
-
query = <<-QUERY
|
31
|
-
SELECT id, #{parent_column}, 1 AS _depth
|
32
|
-
FROM #{self.class.quoted_table_name}
|
33
|
-
WHERE #{arel[:id].eq(parent.try(:id)).to_sql}
|
34
|
-
UNION ALL
|
35
|
-
SELECT alias1.id, alias1.#{parent_column}, _depth + 1
|
36
|
-
FROM #{self.class.quoted_table_name} alias1
|
37
|
-
INNER JOIN ancestors ON alias1.id = ancestors.#{parent_column}
|
38
|
-
QUERY
|
39
|
-
|
40
|
-
with_recursive_join(query, 'ancestors').
|
41
|
-
order('ancestors._depth DESC').
|
42
|
-
extending(Arrangeable)
|
43
|
-
end
|
44
|
-
|
45
|
-
def root
|
46
|
-
root? ? self : ancestors.first
|
47
|
-
end
|
48
|
-
|
49
|
-
def self_and_descendants
|
50
|
-
query = <<-QUERY
|
51
|
-
SELECT id, #{parent_column}, ARRAY[#{position_column}] AS _positions
|
52
|
-
FROM #{self.class.quoted_table_name}
|
53
|
-
WHERE #{arel[:id].eq(id).to_sql}
|
54
|
-
UNION ALL
|
55
|
-
SELECT alias1.id, alias1.#{parent_column}, _positions || alias1.#{position_column}
|
56
|
-
FROM descendants INNER JOIN
|
57
|
-
#{self.class.quoted_table_name} alias1 ON alias1.parent_id = descendants.id
|
58
|
-
QUERY
|
59
|
-
|
60
|
-
with_recursive_join(query, 'descendants').
|
61
|
-
order('descendants._positions ASC').
|
62
|
-
extending(Arrangeable)
|
63
|
-
end
|
64
|
-
|
65
|
-
def descendants
|
66
|
-
self_and_descendants.where(arel[:id].not_eq(id))
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
70
|
-
def recursive_scope
|
71
|
-
ActsAsOrderedTree::Relation::Recursive.new(ordered_tree_scope)
|
72
|
-
end
|
73
|
-
|
74
|
-
def with_recursive_join(recursive_query_sql, aliaz)
|
75
|
-
join_sql = 'INNER JOIN (' +
|
76
|
-
"WITH RECURSIVE #{aliaz} AS (" +
|
77
|
-
recursive_query_sql +
|
78
|
-
") SELECT * FROM #{aliaz} " +
|
79
|
-
") #{aliaz} ON #{aliaz}.id = #{self.class.quoted_table_name}.id"
|
80
|
-
|
81
|
-
ordered_tree_scope.joins(join_sql)
|
82
|
-
end
|
83
|
-
|
84
|
-
# Rails 3.0 does not support update_all with joins, so we patch it :(
|
85
|
-
if ActiveRecord::VERSION::STRING <= '3.1.0'
|
86
|
-
module Rails30UpdateAllPatch
|
87
|
-
def update_all(updates, conditions = nil, options = {})
|
88
|
-
relation = except(:joins, :where).
|
89
|
-
where(:id => select(klass.arel_table[:id]).except(:order, :limit).arel)
|
90
|
-
relation.update_all(updates, conditions, options)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def with_recursive_join_30(recursive_query_sql, aliaz)
|
95
|
-
relation = with_recursive_join_31(recursive_query_sql, aliaz)
|
96
|
-
relation.extend(Rails30UpdateAllPatch)
|
97
|
-
relation
|
98
|
-
end
|
99
|
-
alias_method :with_recursive_join_31, :with_recursive_join
|
100
|
-
alias_method :with_recursive_join, :with_recursive_join_30
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
module ActsAsOrderedTree
|
2
|
-
module Arrangeable
|
3
|
-
# @api private
|
4
|
-
class Arranger
|
5
|
-
attr_reader :collection, :cache
|
6
|
-
|
7
|
-
def initialize(collection, options = {})
|
8
|
-
@collection = collection
|
9
|
-
@discard_orphans = options[:orphans] == :discard
|
10
|
-
@min_level = nil
|
11
|
-
|
12
|
-
if discard_orphans? && !collection.klass.depth_column && ActiveRecord::Base.logger
|
13
|
-
ActiveRecord::Base.logger.warn {
|
14
|
-
'%s model has no `depth` column, '\
|
15
|
-
'it can lead to N+1 queries during #arrange method invocation' % collection.klass
|
16
|
-
}
|
17
|
-
end
|
18
|
-
|
19
|
-
@cache = Hash.new
|
20
|
-
@prepared = false
|
21
|
-
end
|
22
|
-
|
23
|
-
def arrange
|
24
|
-
prepare unless prepared?
|
25
|
-
|
26
|
-
@arranged ||= collection.each_with_object(Hash.new) do |node, result|
|
27
|
-
ancestors = ancestors(node)
|
28
|
-
|
29
|
-
if discard_orphans?
|
30
|
-
root = ancestors.first || node
|
31
|
-
|
32
|
-
next if root.level > @min_level
|
33
|
-
end
|
34
|
-
|
35
|
-
insertion_point = result
|
36
|
-
|
37
|
-
ancestors.each { |a| insertion_point = (insertion_point[a] ||= {}) }
|
38
|
-
|
39
|
-
insertion_point[node] = {}
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
def prepare
|
45
|
-
collection.each do |node|
|
46
|
-
cache[node.id] = node if node.id
|
47
|
-
@min_level = [@min_level, node.level].compact.min
|
48
|
-
end
|
49
|
-
|
50
|
-
@prepared = true
|
51
|
-
end
|
52
|
-
|
53
|
-
def discard_orphans?
|
54
|
-
@discard_orphans
|
55
|
-
end
|
56
|
-
|
57
|
-
def prepared?
|
58
|
-
@prepared
|
59
|
-
end
|
60
|
-
|
61
|
-
# get parent node of +node+
|
62
|
-
def parent(node)
|
63
|
-
cache[node[node.parent_column]]
|
64
|
-
end
|
65
|
-
|
66
|
-
def ancestors(node)
|
67
|
-
parent = parent(node)
|
68
|
-
parent ? ancestors(parent) + [parent] : []
|
69
|
-
end
|
70
|
-
end
|
71
|
-
private_constant :Arranger
|
72
|
-
|
73
|
-
# Arrange associated collection into a nested hash of the form
|
74
|
-
# {node => children}, where children = {} if the node has no children.
|
75
|
-
def arrange(options = {})
|
76
|
-
@arranger ||= Arranger.new(self, options)
|
77
|
-
@arranger.arrange
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
require "acts_as_ordered_tree/adapters/postgresql_adapter"
|
2
|
-
|
3
|
-
module ActsAsOrderedTree
|
4
|
-
module ClassMethods
|
5
|
-
extend ActiveSupport::Concern
|
6
|
-
|
7
|
-
included do
|
8
|
-
scope :preorder, -> { order(arel_table[position_column].asc) }
|
9
|
-
scope :roots, -> { where(arel_table[parent_column].eq(nil)).preorder }
|
10
|
-
|
11
|
-
# add +leaves+ scope only if counter_cache column present
|
12
|
-
scope :leaves, -> { where(arel_table[children_counter_cache_column].eq(0)) } if
|
13
|
-
children_counter_cache?
|
14
|
-
|
15
|
-
# when default value for counter_cache is absent we should set it manually
|
16
|
-
before_create "self.#{children_counter_cache_column} = 0" if children_counter_cache?
|
17
|
-
end
|
18
|
-
|
19
|
-
module ClassMethods
|
20
|
-
# Returns the first root
|
21
|
-
def root
|
22
|
-
roots.first
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
def children_counter_cache? #:nodoc:
|
27
|
-
children_counter_cache_column && columns_hash.key?(children_counter_cache_column.to_s)
|
28
|
-
end
|
29
|
-
|
30
|
-
def setup_ordered_tree_adapter #:nodoc:
|
31
|
-
include "ActsAsOrderedTree::Adapters::#{connection.class.name.demodulize}".constantize
|
32
|
-
rescue NameError, LoadError
|
33
|
-
# ignore
|
34
|
-
end
|
35
|
-
|
36
|
-
def setup_ordered_tree_callbacks #:nodoc:
|
37
|
-
define_model_callbacks :move, :reorder
|
38
|
-
|
39
|
-
if depth_column
|
40
|
-
before_create :set_depth!
|
41
|
-
before_save :set_depth!, :if => "#{parent_column}_changed?".to_sym
|
42
|
-
around_move :update_descendants_depth
|
43
|
-
end
|
44
|
-
|
45
|
-
if children_counter_cache_column
|
46
|
-
around_move :update_counter_cache
|
47
|
-
end
|
48
|
-
|
49
|
-
unless scope_column_names.empty?
|
50
|
-
before_save :set_scope!, :unless => :root?
|
51
|
-
end
|
52
|
-
|
53
|
-
after_save :move_to_root, :unless => [position_column, parent_column]
|
54
|
-
after_save 'move_to_child_of(parent)', :if => parent_column, :unless => position_column
|
55
|
-
after_save "move_to_child_with_index(parent, #{position_column})",
|
56
|
-
:if => "#{position_column} && (#{position_column}_changed? || #{parent_column}_changed?)"
|
57
|
-
|
58
|
-
before_destroy :flush_descendants
|
59
|
-
after_destroy "decrement_lower_positions(#{parent_column}_was, #{position_column}_was)", :if => position_column
|
60
|
-
end
|
61
|
-
|
62
|
-
def setup_ordered_tree_validations #:nodoc:
|
63
|
-
unless scope_column_names.empty?
|
64
|
-
validates_with Validators::ScopeValidator, :on => :update, :unless => :root?
|
65
|
-
end
|
66
|
-
|
67
|
-
# setup validations
|
68
|
-
validates_with Validators::CyclicReferenceValidator, :on => :update, :if => :parent
|
69
|
-
end
|
70
|
-
end # module ClassMethods
|
71
|
-
end # module ClassMethods
|
72
|
-
end # module ActsAsOrderedTree
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module ActsAsOrderedTree
|
2
|
-
module Relation
|
3
|
-
class Base < ActiveRecord::Relation
|
4
|
-
EMPTY_SCOPE_METHOD = ActiveRecord::VERSION::STRING < '4.0.0' ? :scoped : :all
|
5
|
-
|
6
|
-
# Create from existing +relation+ or from +class+ and +table+
|
7
|
-
def initialize(class_or_relation, table = nil)
|
8
|
-
relation = class_or_relation
|
9
|
-
|
10
|
-
if class_or_relation.is_a?(Class)
|
11
|
-
relation = class_or_relation.send(EMPTY_SCOPE_METHOD)
|
12
|
-
table ||= class_or_relation.arel_table
|
13
|
-
|
14
|
-
super(class_or_relation, table)
|
15
|
-
else
|
16
|
-
super(class_or_relation.klass, class_or_relation.table)
|
17
|
-
end
|
18
|
-
|
19
|
-
# copy instance variables from real relation
|
20
|
-
relation.instance_variables.each do |ivar|
|
21
|
-
instance_variable_set(ivar, relation.instance_variable_get(ivar))
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module ActsAsOrderedTree
|
2
|
-
module TenaciousTransaction
|
3
|
-
DEADLOCK_MESSAGES = /Deadlock found when trying to get lock|Lock wait timeout exceeded|deadlock detected/.freeze
|
4
|
-
RETRY_COUNT = 10
|
5
|
-
|
6
|
-
# Partially borrowed from awesome_nested_set
|
7
|
-
def tenacious_transaction(&block) #:nodoc:
|
8
|
-
return transaction(&block) if @in_tenacious_transaction
|
9
|
-
|
10
|
-
@in_tenacious_transaction = true
|
11
|
-
retry_count = 0
|
12
|
-
begin
|
13
|
-
transaction(&block)
|
14
|
-
rescue ActiveRecord::StatementInvalid => error
|
15
|
-
raise unless self.class.connection.open_transactions.zero?
|
16
|
-
raise unless error.message =~ DEADLOCK_MESSAGES
|
17
|
-
raise unless retry_count < RETRY_COUNT
|
18
|
-
retry_count += 1
|
19
|
-
|
20
|
-
logger.info "Deadlock detected on retry #{retry_count}, restarting transaction"
|
21
|
-
|
22
|
-
sleep(rand(retry_count)*0.1) # Aloha protocol
|
23
|
-
|
24
|
-
retry
|
25
|
-
ensure
|
26
|
-
@in_tenacious_transaction = false
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,156 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
# FIXME: These tests are buggy on Rails 3.0
|
4
|
-
# Sqlite is not concurrent database
|
5
|
-
if ActiveRecord::VERSION::STRING >= "3.1" && ENV['DB'] != 'sqlite3'
|
6
|
-
describe ActsAsOrderedTree, :non_transactional do
|
7
|
-
module Concurrency
|
8
|
-
# run block in its own thread, create +size+ threads
|
9
|
-
def pool(size)
|
10
|
-
size.times.map { |x|
|
11
|
-
Thread.new do
|
12
|
-
ActiveRecord::Base.connection_pool.with_connection { yield x }
|
13
|
-
end
|
14
|
-
}.each(&:join)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
include Concurrency
|
18
|
-
|
19
|
-
let!(:root) { create :default }
|
20
|
-
|
21
|
-
it "should not create nodes with same position" do
|
22
|
-
pool(3) do
|
23
|
-
create :default, :parent => root
|
24
|
-
end
|
25
|
-
|
26
|
-
root.children.map(&:position).should eq [1, 2, 3]
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should not move nodes to same position when moving to child of certain node" do
|
30
|
-
nodes = create_list :default, 3
|
31
|
-
|
32
|
-
pool(3) do |x|
|
33
|
-
nodes[x].move_to_child_of(root)
|
34
|
-
end
|
35
|
-
|
36
|
-
root.children.map(&:position).should eq [1, 2, 3]
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should not move nodes to same position when moving to left of root node" do
|
40
|
-
nodes = create_list :default, 3, :parent => root
|
41
|
-
|
42
|
-
pool(3) do |x|
|
43
|
-
nodes[x].move_to_left_of(root)
|
44
|
-
end
|
45
|
-
|
46
|
-
Default.roots.map(&:position).should eq [1, 2, 3, 4]
|
47
|
-
end
|
48
|
-
|
49
|
-
it "should not move nodes to same position when moving to left of child node" do
|
50
|
-
child = create :default, :parent => root
|
51
|
-
nodes = create_list :default, 3, :parent => child
|
52
|
-
|
53
|
-
pool(3) do |x|
|
54
|
-
nodes[x].move_to_left_of(child)
|
55
|
-
end
|
56
|
-
|
57
|
-
root.children.map(&:position).should eq [1, 2, 3, 4]
|
58
|
-
root.children.last.should eq child
|
59
|
-
end
|
60
|
-
|
61
|
-
it "should not move nodes to same position when moving to right of child node" do
|
62
|
-
child = create :default, :parent => root
|
63
|
-
nodes = create_list :default, 3, :parent => child
|
64
|
-
|
65
|
-
pool(3) do |x|
|
66
|
-
nodes[x].move_to_right_of(child)
|
67
|
-
end
|
68
|
-
|
69
|
-
root.children.map(&:position).should eq [1, 2, 3, 4]
|
70
|
-
root.children.first.should eq child
|
71
|
-
end
|
72
|
-
|
73
|
-
it "should not move nodes to same position when moving to root" do
|
74
|
-
nodes = create_list :default, 3, :parent => root
|
75
|
-
|
76
|
-
pool(3) do |x|
|
77
|
-
nodes[x].move_to_root
|
78
|
-
end
|
79
|
-
|
80
|
-
Default.roots.map(&:position).should eq [1, 2, 3, 4]
|
81
|
-
end
|
82
|
-
|
83
|
-
# checking deadlock also
|
84
|
-
it "should not move nodes to same position when moving to specified index" do
|
85
|
-
# root
|
86
|
-
# * child1
|
87
|
-
# * nodes1_1
|
88
|
-
# * nodes1_2
|
89
|
-
# * child2
|
90
|
-
# * nodes2_1
|
91
|
-
# * nodes2_2
|
92
|
-
child1, child2 = create_list :default, 2, :parent => root
|
93
|
-
|
94
|
-
nodes1, nodes2 = create_list(:default, 2, :parent => child1),
|
95
|
-
create_list(:default, 2, :parent => child2)
|
96
|
-
|
97
|
-
nodes1_1, nodes1_2 = nodes1
|
98
|
-
nodes2_1, nodes2_2 = nodes2
|
99
|
-
|
100
|
-
# nodes2_2 -> child1[0]
|
101
|
-
thread1 = Thread.new do
|
102
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
103
|
-
nodes2_2.move_to_child_with_index(child1, 0)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
# nodes1_1 -> child2[2]
|
107
|
-
thread2 = Thread.new do
|
108
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
109
|
-
nodes1_1.move_to_child_with_index(child2, 2)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
[thread1, thread2].map(&:join)
|
113
|
-
|
114
|
-
child1.children.reload.should == [nodes2_2, nodes1_2]
|
115
|
-
child2.children.reload.should == [nodes2_1, nodes1_1]
|
116
|
-
end
|
117
|
-
|
118
|
-
it "should not move nodes to same position when moving higher" do
|
119
|
-
child1, child2, child3 = create_list :default, 3, :parent => root
|
120
|
-
|
121
|
-
thread1 = Thread.new do
|
122
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
123
|
-
child2.move_higher
|
124
|
-
end
|
125
|
-
end
|
126
|
-
thread2 = Thread.new do
|
127
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
128
|
-
child3.move_higher
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
[thread1, thread2].map(&:join)
|
133
|
-
|
134
|
-
root.children.map(&:position).should eq [1, 2, 3]
|
135
|
-
end
|
136
|
-
|
137
|
-
it "should not move nodes to same position when moving lower" do
|
138
|
-
child1, child2, child3 = create_list :default, 3, :parent => root
|
139
|
-
|
140
|
-
thread1 = Thread.new do
|
141
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
142
|
-
child1.move_lower
|
143
|
-
end
|
144
|
-
end
|
145
|
-
thread2 = Thread.new do
|
146
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
147
|
-
child2.move_lower
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
[thread1, thread2].map(&:join)
|
152
|
-
|
153
|
-
root.children.map(&:position).should eq [1, 2, 3]
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|