simple_nested_set 0.0.4 → 0.0.5

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.
@@ -0,0 +1,8 @@
1
+ class Hash
2
+ def extract_nested_set_attributes!
3
+ slice(*SimpleNestedSet::ATTRIBUTES).tap do
4
+ except!(*SimpleNestedSet::ATTRIBUTES)
5
+ end
6
+ end
7
+ end
8
+
@@ -4,15 +4,16 @@ module SimpleNestedSet
4
4
  return if acts_as_nested_set?
5
5
 
6
6
  include SimpleNestedSet::InstanceMethods
7
- extend SimpleNestedSet::ClassMethods
7
+ extend SimpleNestedSet::ClassMethods
8
8
 
9
9
  define_callbacks :move, :terminator => "result == false"
10
10
 
11
- before_validation lambda { |r| r.nested_set.init_as_node }
11
+ before_validation lambda { |r| r.nested_set.init_as_node }, :if => :new_record?
12
12
  before_destroy lambda { |r| r.nested_set.prune_branch }
13
+ after_save lambda { |r| r.nested_set.save! }
13
14
 
14
15
  belongs_to :parent, :class_name => self.name
15
- has_many :children, :foreign_key => :parent_id, :class_name => class_of_active_record_descendant(self).name
16
+ has_many :children, :foreign_key => :parent_id, :class_name => self.name
16
17
 
17
18
  default_scope :order => :lft
18
19
 
@@ -10,14 +10,6 @@ module SimpleNestedSet
10
10
  set_callback(:move, :after, *args, &block)
11
11
  end
12
12
 
13
- def create(attributes)
14
- nested_set_class.with_move_by_attributes(attributes) { super }
15
- end
16
-
17
- def create!(attributes)
18
- nested_set_class.with_move_by_attributes(attributes) { super }
19
- end
20
-
21
13
  # Returns the first root node (with the given scope if any)
22
14
  def root(scope = nil)
23
15
  roots(scope).first
@@ -3,16 +3,12 @@ require 'active_support/core_ext/hash/keys'
3
3
  module SimpleNestedSet
4
4
  module InstanceMethods
5
5
  def nested_set
6
- @nested_set ||= nested_set_class.new(self)
6
+ @_nested_set ||= nested_set_class.new(self)
7
7
  end
8
8
 
9
- # TODO refactor
10
- def update_attributes(attributes)
11
- nested_set.with_move_by_attributes(attributes) { super }
12
- end
13
-
14
- def update_attributes!(attributes)
15
- nested_set.with_move_by_attributes(attributes) { super }
9
+ def attributes=(attributes)
10
+ @_nested_set_attributes = attributes.extract_nested_set_attributes!
11
+ super
16
12
  end
17
13
 
18
14
  # recursively populates the parent and children associations of self and
@@ -22,11 +18,6 @@ module SimpleNestedSet
22
18
  self
23
19
  end
24
20
 
25
- # Returns the level of this object in the tree, root level is 0
26
- def level
27
- parent_id.nil? ? 0 : ancestors.count
28
- end
29
-
30
21
  # Returns true if this is a root node.
31
22
  def root?
32
23
  parent_id.blank?
@@ -38,6 +38,10 @@ module SimpleNestedSet
38
38
  attributes.symbolize_keys!
39
39
  attributes.each { |key, value| attributes[key] = nil if value == 'null' }
40
40
 
41
+ [:parent, :left, :right].each do |key|
42
+ attributes[:"#{key}_id"] = attributes.delete(key).id if attributes.key?(key)
43
+ end
44
+
41
45
  # if left_id is given but blank, set right_id to leftmost sibling
42
46
  attributes[:right_id] = siblings.first.id if blank_given?(:left_id) && siblings.any?
43
47
 
@@ -15,17 +15,66 @@ module SimpleNestedSet
15
15
 
16
16
  def perform
17
17
  node.run_callbacks(:move) do
18
- # reload
19
18
  unless bound == node.rgt || bound == node.lft # there would be no change
20
19
  nested_set.transaction do
21
- # node.save!
22
- nested_set.update_all(query)
20
+ # puts ActiveRecord::Base.send(:sanitize_sql_array, query)
21
+ update_structure!
22
+ update_denormalizations!
23
23
  end
24
24
  end
25
25
  reload
26
26
  end
27
27
  end
28
28
 
29
+ def update_structure!
30
+ sql = <<-sql
31
+ lft = CASE
32
+ WHEN lft BETWEEN :a AND :b THEN lft + :d - :b
33
+ WHEN lft BETWEEN :c AND :d THEN lft + :a - :c
34
+ ELSE lft END,
35
+
36
+ rgt = CASE
37
+ WHEN rgt BETWEEN :a AND :b THEN rgt + :d - :b
38
+ WHEN rgt BETWEEN :c AND :d THEN rgt + :a - :c
39
+ ELSE rgt END,
40
+
41
+ parent_id = CASE
42
+ WHEN id = :id THEN :parent_id
43
+ ELSE parent_id END
44
+ sql
45
+
46
+ a, b, c, d = boundaries
47
+ sql = [sql, { :a => a, :b => b, :c => c, :d => d, :id => id, :parent_id => parent_id }]
48
+ nested_set.update_all(sql)
49
+ end
50
+
51
+ def update_denormalizations!
52
+ sql = []
53
+ sql << denormalize_level_query if node.has_attribute?(:level)
54
+ sql << denormalize_path_query if node.has_attribute?(:path)
55
+ nested_set.update_all(sql.join(',')) unless sql.blank?
56
+ end
57
+
58
+ def denormalize_level_query
59
+ <<-sql
60
+ level = (
61
+ SELECT count(id)
62
+ FROM #{table_name} as l
63
+ WHERE l.lft < #{table_name}.lft AND l.rgt > #{table_name}.rgt
64
+ )
65
+ sql
66
+ end
67
+
68
+ def denormalize_path_query
69
+ <<-sql
70
+ path = (
71
+ SELECT GROUP_CONCAT(slug, '/')
72
+ FROM #{table_name} as l
73
+ WHERE l.lft <= #{table_name}.lft AND l.rgt >= #{table_name}.rgt AND parent_id IS NOT NULL
74
+ )
75
+ sql
76
+ end
77
+
29
78
  def reload
30
79
  target.nested_set.reload if target
31
80
  node.nested_set.reload
@@ -66,33 +115,6 @@ module SimpleNestedSet
66
115
  @roots ||= node.nested_set.roots
67
116
  end
68
117
 
69
- def query
70
- sql = <<-sql
71
- lft = CASE
72
- WHEN lft BETWEEN :a AND :b THEN lft + :d - :b
73
- WHEN lft BETWEEN :c AND :d THEN lft + :a - :c
74
- ELSE lft END,
75
-
76
- rgt = CASE
77
- WHEN rgt BETWEEN :a AND :b THEN rgt + :d - :b
78
- WHEN rgt BETWEEN :c AND :d THEN rgt + :a - :c
79
- ELSE rgt END,
80
-
81
- parent_id = CASE
82
- WHEN id = :id THEN :parent_id
83
- ELSE parent_id END,
84
-
85
- level = (
86
- SELECT count(id)
87
- FROM #{table_name} as t
88
- WHERE t.lft < #{table_name}.lft AND rgt > #{table_name}.rgt
89
- )
90
- sql
91
- # TODO name a, b, c, d in a more reasonable way
92
- a, b, c, d = boundaries
93
- [sql, { :a => a, :b => b, :c => c, :d => d, :id => id, :parent_id => parent_id }]
94
- end
95
-
96
118
  def table_name
97
119
  node.class.quoted_table_name
98
120
  end
@@ -1,7 +1,5 @@
1
1
  module SimpleNestedSet
2
2
  class NestedSet < ActiveRecord::Relation
3
- NESTED_SET_ATTRIBUTES = [:parent_id, :left_id, :right_id]
4
-
5
3
  class_inheritable_accessor :node_class, :scope_names
6
4
 
7
5
  class << self
@@ -21,24 +19,6 @@ module SimpleNestedSet
21
19
  c.merge(name => scope.respond_to?(name) ? scope.send(name) : scope[name])
22
20
  end
23
21
  end
24
-
25
- def with_move_by_attributes(attributes, node = nil)
26
- node_class.transaction do
27
- nested_set_attributes = extract_nested_set_attributes!(attributes)
28
- yield.tap do |result|
29
- unless nested_set_attributes.empty?
30
- node ||= result
31
- node.nested_set.move_by_attributes(nested_set_attributes)
32
- end
33
- end
34
- end
35
- end
36
-
37
- def extract_nested_set_attributes!(attributes)
38
- result = attributes.slice(*NESTED_SET_ATTRIBUTES)
39
- attributes.except!(*NESTED_SET_ATTRIBUTES)
40
- result
41
- end
42
22
  end
43
23
 
44
24
  attr_reader :node
@@ -48,9 +28,11 @@ module SimpleNestedSet
48
28
  @node = args.first if args.size == 1
49
29
  @where_values = self.class.scope(node).instance_variable_get(:@where_values) if node
50
30
  end
51
-
52
- def with_move_by_attributes(attributes, &block)
53
- self.class.with_move_by_attributes(attributes, node, &block)
31
+
32
+ def save!
33
+ attributes = node.instance_variable_get(:@_nested_set_attributes)
34
+ node.instance_variable_set(:@_nested_set_attributes, nil)
35
+ move_by_attributes(attributes) unless attributes.blank?
54
36
  end
55
37
 
56
38
  # Returns true if the node has the same scope as the given node
@@ -60,7 +42,7 @@ module SimpleNestedSet
60
42
 
61
43
  # reload left, right, and parent
62
44
  def reload
63
- node.reload(:select => 'lft, rgt, parent_id')
45
+ node.reload(:select => 'lft, rgt, parent_id') unless node.new_record?
64
46
  end
65
47
 
66
48
  def populate_associations(nodes)
@@ -74,11 +56,8 @@ module SimpleNestedSet
74
56
 
75
57
  # before validation set lft and rgt to the end of the tree
76
58
  def init_as_node
77
- unless node.rgt && node.lft
78
- max_right = maximum(:rgt) || 0
79
- node.lft, node.rgt = max_right + 1, max_right + 2
80
- end
81
- true
59
+ max_right = maximum(:rgt) || 0
60
+ node.lft, node.rgt = max_right + 1, max_right + 2
82
61
  end
83
62
 
84
63
  # Prunes a branch off of the tree, shifting all of the elements on the right
@@ -1,3 +1,3 @@
1
1
  module SimpleNestedSet
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -1,6 +1,9 @@
1
1
  require 'active_record'
2
+ require 'core_ext/hash/extract_nested_set_attributes'
2
3
 
3
4
  module SimpleNestedSet
5
+ ATTRIBUTES = [:parent, :parent_id, :left_id, :right_id]
6
+
4
7
  autoload :ActMacro, 'simple_nested_set/act_macro'
5
8
  autoload :ClassMethods, 'simple_nested_set/class_methods'
6
9
  autoload :InstanceMethods, 'simple_nested_set/instance_methods'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_nested_set
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 4
10
- version: 0.0.4
9
+ - 5
10
+ version: 0.0.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sven Fuchs
@@ -100,6 +100,7 @@ extensions: []
100
100
  extra_rdoc_files: []
101
101
 
102
102
  files:
103
+ - lib/core_ext/hash/extract_nested_set_attributes.rb
103
104
  - lib/simple_nested_set.rb
104
105
  - lib/simple_nested_set/act_macro.rb
105
106
  - lib/simple_nested_set/class_methods.rb