acts_as_ordered_tree 1.3.1 → 2.0.0.beta3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
@@ -0,0 +1,67 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'acts_as_ordered_tree/position'
|
4
|
+
require 'acts_as_ordered_tree/transaction/create'
|
5
|
+
require 'acts_as_ordered_tree/transaction/destroy'
|
6
|
+
require 'acts_as_ordered_tree/transaction/move'
|
7
|
+
require 'acts_as_ordered_tree/transaction/passthrough'
|
8
|
+
require 'acts_as_ordered_tree/transaction/reorder'
|
9
|
+
|
10
|
+
module ActsAsOrderedTree
|
11
|
+
module Transaction
|
12
|
+
# @api private
|
13
|
+
module Factory
|
14
|
+
# Creates previous and current position objects for node
|
15
|
+
# @api private
|
16
|
+
class PositionFactory
|
17
|
+
def initialize(node)
|
18
|
+
@node = node
|
19
|
+
end
|
20
|
+
|
21
|
+
def previous
|
22
|
+
Position.new @node, @node.parent_id_was, @node.position_was
|
23
|
+
end
|
24
|
+
|
25
|
+
def current
|
26
|
+
Position.new @node, @node.parent_id, @node.position
|
27
|
+
end
|
28
|
+
|
29
|
+
def transition
|
30
|
+
Position::Transition.new(previous, current)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
private_constant :PositionFactory
|
34
|
+
|
35
|
+
# Creates proper transaction according to +node+
|
36
|
+
#
|
37
|
+
# @param [ActsAsOrderedTree::Node] node
|
38
|
+
# @param [true, false] destroy set to true if node should be destroyed
|
39
|
+
# @return [ActsAsOrderedTree::Transaction::Base]
|
40
|
+
def create(node, destroy = false)
|
41
|
+
pos = PositionFactory.new(node)
|
42
|
+
|
43
|
+
case
|
44
|
+
when destroy
|
45
|
+
Destroy.new(node, pos.previous)
|
46
|
+
when node.record.new_record?
|
47
|
+
Create.new(node, pos.current)
|
48
|
+
else
|
49
|
+
create_from_transition(node, pos.transition)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
module_function :create
|
53
|
+
|
54
|
+
def create_from_transition(node, transition)
|
55
|
+
case
|
56
|
+
when transition.movement?
|
57
|
+
Move.new(node, transition)
|
58
|
+
when transition.reorder?
|
59
|
+
Reorder.new(node, transition)
|
60
|
+
else
|
61
|
+
Passthrough.new
|
62
|
+
end
|
63
|
+
end
|
64
|
+
module_function :create_from_transition
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'acts_as_ordered_tree/transaction/update'
|
4
|
+
|
5
|
+
module ActsAsOrderedTree
|
6
|
+
module Transaction
|
7
|
+
class Move < Update
|
8
|
+
before 'trigger_callback(:before_remove, from.parent)'
|
9
|
+
before 'trigger_callback(:before_add, to.parent)'
|
10
|
+
|
11
|
+
after :update_descendants_depth, :if => [
|
12
|
+
'transition.movement?',
|
13
|
+
'tree.columns.depth?',
|
14
|
+
'transition.level_changed?',
|
15
|
+
'record.children.size > 0'
|
16
|
+
]
|
17
|
+
|
18
|
+
after 'trigger_callback(:after_add, to.parent)'
|
19
|
+
after 'trigger_callback(:after_remove, from.parent)'
|
20
|
+
after 'transition.update_counters'
|
21
|
+
|
22
|
+
finalize
|
23
|
+
|
24
|
+
private
|
25
|
+
def update_values
|
26
|
+
updates = Hash[
|
27
|
+
position => position_value,
|
28
|
+
parent_id => parent_id_value
|
29
|
+
]
|
30
|
+
|
31
|
+
updates[depth] = depth_value if tree.columns.depth? && transition.level_changed?
|
32
|
+
|
33
|
+
updates
|
34
|
+
end
|
35
|
+
|
36
|
+
# Records to be updated
|
37
|
+
def update_scope
|
38
|
+
filter = (id == record.id) | (parent_id == from.parent_id) | (parent_id == to.parent_id)
|
39
|
+
node.scope.where(filter.to_sql)
|
40
|
+
end
|
41
|
+
|
42
|
+
def parent_id_value
|
43
|
+
switch.when(id == record.id, to.parent_id).else(parent_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def position_value
|
47
|
+
switch.
|
48
|
+
when(id == record.id).
|
49
|
+
then(@to.position).
|
50
|
+
# decrement lower positions in old parent
|
51
|
+
when((parent_id == from.parent_id) & (position > from.position)).
|
52
|
+
then(position - 1).
|
53
|
+
# increment positions in new parent
|
54
|
+
when((parent_id == to.parent_id) & (position >= to.position)).
|
55
|
+
then(position + 1).
|
56
|
+
else(position)
|
57
|
+
end
|
58
|
+
|
59
|
+
def depth_value
|
60
|
+
switch.
|
61
|
+
when(id == record.id, to.depth).
|
62
|
+
else(depth)
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_descendants_depth
|
66
|
+
record.descendants.update_all set depth => depth + (to.depth - from.depth)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'acts_as_ordered_tree/transaction/update'
|
4
|
+
|
5
|
+
module ActsAsOrderedTree
|
6
|
+
module Transaction
|
7
|
+
class Reorder < Update
|
8
|
+
finalize
|
9
|
+
|
10
|
+
protected
|
11
|
+
# if we reorder node then we cannot put it to position higher than highest
|
12
|
+
def push_to_bottom
|
13
|
+
to.position = highest_position.zero? ? 1 : highest_position
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def update_scope
|
18
|
+
to.siblings.where(positions_range)
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_values
|
22
|
+
{ position => position_value }
|
23
|
+
end
|
24
|
+
|
25
|
+
def positions_range
|
26
|
+
position.in([from.position, to.position].min..[from.position, to.position].max)
|
27
|
+
end
|
28
|
+
|
29
|
+
def position_value
|
30
|
+
expr = switch.
|
31
|
+
when(position == from.position).then(to.position).
|
32
|
+
else(position)
|
33
|
+
|
34
|
+
if to.position > from.position
|
35
|
+
expr.when(positions_range).then(position - 1)
|
36
|
+
else
|
37
|
+
expr.when(positions_range).then(position + 1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end # class Reorder
|
41
|
+
end # module Transaction
|
42
|
+
end # module ActsAsOrderedTree
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'acts_as_ordered_tree/transaction/base'
|
4
|
+
|
5
|
+
module ActsAsOrderedTree
|
6
|
+
module Transaction
|
7
|
+
class Save < Base
|
8
|
+
attr_reader :to
|
9
|
+
|
10
|
+
before 'to.lock!'
|
11
|
+
before :set_scope!, :if => 'to.parent?'
|
12
|
+
before :push_to_bottom, :if => :push_to_bottom?
|
13
|
+
before 'to.position = 1', :if => 'to.position <= 0'
|
14
|
+
|
15
|
+
around :copy_attributes
|
16
|
+
|
17
|
+
# @param [ActsAsOrderedTree::Node] node
|
18
|
+
# @param [ActsAsOrderedTree::Position] to to which position given +node+ is saved
|
19
|
+
def initialize(node, to)
|
20
|
+
super(node)
|
21
|
+
@to = to
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
# Copies parent_id, position and depth from destination to record
|
26
|
+
def copy_attributes
|
27
|
+
record.parent = to.parent
|
28
|
+
node.position = to.position
|
29
|
+
node.depth = to.depth if tree.columns.depth?
|
30
|
+
|
31
|
+
yield
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns highest position within node's siblings
|
35
|
+
def highest_position
|
36
|
+
@highest_position ||= to.siblings.maximum(tree.columns.position) || 0
|
37
|
+
end
|
38
|
+
|
39
|
+
# Should be fired when given position is empty
|
40
|
+
def push_to_bottom
|
41
|
+
to.position = highest_position + 1
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns true if record should be pushed to bottom of list
|
45
|
+
def push_to_bottom?
|
46
|
+
to.position.blank? ||
|
47
|
+
position_out_of_bounds?
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def set_scope!
|
52
|
+
tree.columns.scope.each do |column|
|
53
|
+
record[column] = to.parent[column]
|
54
|
+
end
|
55
|
+
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def position_out_of_bounds?
|
60
|
+
to.position > highest_position
|
61
|
+
end
|
62
|
+
end # class Save
|
63
|
+
end # module Transaction
|
64
|
+
end # module ActsAsOrderedTree
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'active_support/core_ext/object/with_options'
|
4
|
+
|
5
|
+
require 'acts_as_ordered_tree/position'
|
6
|
+
require 'acts_as_ordered_tree/transaction/save'
|
7
|
+
require 'acts_as_ordered_tree/transaction/dsl'
|
8
|
+
|
9
|
+
module ActsAsOrderedTree
|
10
|
+
module Transaction
|
11
|
+
# Update transaction includes Move and Reorder
|
12
|
+
#
|
13
|
+
# @abstract
|
14
|
+
# @api private
|
15
|
+
class Update < Save
|
16
|
+
include DSL
|
17
|
+
|
18
|
+
attr_reader :from, :transition
|
19
|
+
|
20
|
+
around :update_tree
|
21
|
+
|
22
|
+
# @param [ActsAsOrderedTree::Node] node
|
23
|
+
# @param [ActsAsOrderedTree::Position::Transition] transition
|
24
|
+
def initialize(node, transition)
|
25
|
+
@transition = transition
|
26
|
+
@from = transition.from
|
27
|
+
|
28
|
+
super(node, transition.to)
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
def update_tree
|
33
|
+
callbacks = transition.reorder? ? :reorder : :move
|
34
|
+
|
35
|
+
record.run_callbacks(callbacks) do
|
36
|
+
record.hook_update do |update|
|
37
|
+
update.scope = update_scope
|
38
|
+
update.values = update_values.merge(changed_attributes)
|
39
|
+
|
40
|
+
yield
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def update_scope
|
46
|
+
# implement in successors
|
47
|
+
end
|
48
|
+
|
49
|
+
def update_values
|
50
|
+
# implement in successors
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
# Returns hash of UPDATE..SET expressions for each
|
55
|
+
# changed record attribute (except tree attributes)
|
56
|
+
#
|
57
|
+
# @return [Hash<String => Arel::Nodes::Node>]
|
58
|
+
def changed_attributes
|
59
|
+
changed_attributes_names.each_with_object({}) do |attr, hash|
|
60
|
+
hash[attr] = attribute_value(attr)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def attribute_value(attr)
|
65
|
+
attr_value = record.read_attribute(attr)
|
66
|
+
quoted = record.class.connection.quote(attr_value)
|
67
|
+
|
68
|
+
switch.
|
69
|
+
when(id == record.id).then(Arel.sql(quoted)).
|
70
|
+
else(attribute(attr))
|
71
|
+
end
|
72
|
+
|
73
|
+
def changed_attributes_names
|
74
|
+
record.changed - (tree.columns.to_a - tree.columns.scope)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'acts_as_ordered_tree/compatibility'
|
4
|
+
|
5
|
+
require 'acts_as_ordered_tree/tree/callbacks'
|
6
|
+
require 'acts_as_ordered_tree/tree/columns'
|
7
|
+
require 'acts_as_ordered_tree/tree/children_association'
|
8
|
+
require 'acts_as_ordered_tree/tree/deprecated_columns_accessors'
|
9
|
+
require 'acts_as_ordered_tree/tree/parent_association'
|
10
|
+
require 'acts_as_ordered_tree/tree/perseverance'
|
11
|
+
require 'acts_as_ordered_tree/tree/scopes'
|
12
|
+
|
13
|
+
require 'acts_as_ordered_tree/hooks'
|
14
|
+
|
15
|
+
require 'acts_as_ordered_tree/adapters'
|
16
|
+
require 'acts_as_ordered_tree/validators'
|
17
|
+
|
18
|
+
require 'acts_as_ordered_tree/instance_methods'
|
19
|
+
|
20
|
+
module ActsAsOrderedTree
|
21
|
+
# ActsAsOrderedTree::Tree
|
22
|
+
class Tree
|
23
|
+
# Default ordered tree options
|
24
|
+
DEFAULT_OPTIONS = {
|
25
|
+
:parent_column => :parent_id,
|
26
|
+
:position_column => :position,
|
27
|
+
:depth_column => :depth
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
PROTECTED_ATTRIBUTES = :left_sibling, :left_sibling_id,
|
31
|
+
:higher_item, :higher_item_id,
|
32
|
+
:right_sibling, :right_sibling_id,
|
33
|
+
:lower_item, :lower_item_id
|
34
|
+
|
35
|
+
attr_reader :klass
|
36
|
+
|
37
|
+
# @!attribute [r] columns
|
38
|
+
# Columns information aggregator
|
39
|
+
#
|
40
|
+
# @return [ActsAsOrderedTree::Tree::Columns] column object
|
41
|
+
attr_reader :columns
|
42
|
+
|
43
|
+
# @!attribute [r] callbacks
|
44
|
+
# :before_add, :after_add, :before_remove and :after_remove callbacks storage
|
45
|
+
#
|
46
|
+
# @return [ActsAsOrderedTree::Tree::Callbacks] callbacks object
|
47
|
+
attr_reader :callbacks
|
48
|
+
|
49
|
+
# @!attribute [r] adapter
|
50
|
+
# Ordered tree adapter which contains implementation of some traverse methods
|
51
|
+
#
|
52
|
+
# @return [ActsAsOrderedTree::Adapters::Abstract] adapter object
|
53
|
+
attr_reader :adapter
|
54
|
+
|
55
|
+
# @!attribute [r] options
|
56
|
+
# Ordered tree options
|
57
|
+
#
|
58
|
+
# @return [Hash]
|
59
|
+
attr_reader :options
|
60
|
+
|
61
|
+
# Create and setup tree object
|
62
|
+
#
|
63
|
+
# @param [Class] klass
|
64
|
+
# @param [Hash] options
|
65
|
+
def self.setup!(klass, options)
|
66
|
+
klass.ordered_tree = new(klass, options).setup
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param [Class] klass
|
70
|
+
# @param [Hash] options
|
71
|
+
def initialize(klass, options)
|
72
|
+
@klass = klass
|
73
|
+
@options = DEFAULT_OPTIONS.merge(options).freeze
|
74
|
+
@columns = Columns.new(klass, @options)
|
75
|
+
@callbacks = Callbacks.new(klass, @options)
|
76
|
+
@children = ChildrenAssociation.new(self)
|
77
|
+
@parent = ParentAssociation.new(self)
|
78
|
+
@adapter = Adapters.lookup(klass.connection.adapter_name).new(self)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Builds associations, callbacks, validations etc.
|
82
|
+
def setup
|
83
|
+
setup_associations
|
84
|
+
setup_once
|
85
|
+
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns Class object which will be used for associations,
|
90
|
+
# scopes and tree traversals.
|
91
|
+
#
|
92
|
+
# @return [Class]
|
93
|
+
def base_class
|
94
|
+
if klass.finder_needs_type_condition?
|
95
|
+
klass.base_class
|
96
|
+
else
|
97
|
+
klass
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
def already_setup?
|
103
|
+
klass.ordered_tree?
|
104
|
+
end
|
105
|
+
|
106
|
+
def setup_once
|
107
|
+
return if already_setup?
|
108
|
+
|
109
|
+
setup_validations
|
110
|
+
setup_callbacks
|
111
|
+
protect_attributes *PROTECTED_ATTRIBUTES
|
112
|
+
|
113
|
+
klass.class_eval do
|
114
|
+
extend Scopes
|
115
|
+
extend DeprecatedColumnsAccessors
|
116
|
+
|
117
|
+
include InstanceMethods
|
118
|
+
include Perseverance
|
119
|
+
include Hooks
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def setup_associations
|
124
|
+
@parent.build
|
125
|
+
@children.build
|
126
|
+
end
|
127
|
+
|
128
|
+
def setup_validations
|
129
|
+
if columns.scope?
|
130
|
+
klass.validates_with Validators::ScopeValidator, :on => :update, :if => :parent
|
131
|
+
end
|
132
|
+
|
133
|
+
klass.validates_with Validators::CyclicReferenceValidator, :on => :update, :if => :parent
|
134
|
+
end
|
135
|
+
|
136
|
+
def setup_callbacks
|
137
|
+
klass.define_model_callbacks(:move, :reorder)
|
138
|
+
klass.around_save(:save_ordered_tree_node)
|
139
|
+
klass.around_destroy(:destroy_ordered_tree_node)
|
140
|
+
end
|
141
|
+
|
142
|
+
def protect_attributes(*attributes)
|
143
|
+
Compatibility.version '< 4.0.0' do
|
144
|
+
klass.attr_protected *attributes
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|