Class: SimpleNestedSet::NestedSet

Inherits:
ActiveRecord::Relation
  • Object
show all
Defined in:
lib/simple_nested_set/nested_set.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ NestedSet

Returns a new instance of NestedSet.



26
27
28
29
30
# File 'lib/simple_nested_set/nested_set.rb', line 26

def initialize(*args)
  super(node_class, node_class.arel_table)
  @node = args.first if args.size == 1
  @where_values = self.class.scope(node).instance_variable_get(:@where_values) if node
end

Instance Attribute Details

#nodeObject (readonly)

Returns the value of attribute node.



24
25
26
# File 'lib/simple_nested_set/nested_set.rb', line 24

def node
  @node
end

Class Method Details

.build_class(model, scopes) ⇒ Object



6
7
8
9
10
11
# File 'lib/simple_nested_set/nested_set.rb', line 6

def build_class(model, scopes)
  model.const_get(:NestedSet) rescue model.const_set(:NestedSet, Class.new(NestedSet)).tap do |node_class|
    node_class.node_class = model
    node_class.scope_names = Array(scopes).map { |s| s.to_s =~ /_id$/ ? s.to_sym : :"#{s}_id" }
  end
end

.scope(scope) ⇒ Object



13
14
15
# File 'lib/simple_nested_set/nested_set.rb', line 13

def scope(scope)
  scope.blank? ? node_class.scoped : node_class.where(scope_condition(scope))
end

.scope_condition(scope) ⇒ Object



17
18
19
20
21
# File 'lib/simple_nested_set/nested_set.rb', line 17

def scope_condition(scope)
  scope_names.inject({}) do |c, name|
    c.merge(name => scope.respond_to?(name) ? scope.send(name) : scope[name])
  end
end

Instance Method Details

#attribute_namesObject



58
59
60
# File 'lib/simple_nested_set/nested_set.rb', line 58

def attribute_names
  @attribute_names ||= node.attribute_names.select { |attribute| ATTRIBUTES.include?(attribute.to_sym) }
end

#denormalize!Object

FIXME we don’t always want to call this on after_save, do we? it’s only relevant when either the structure or the slug has changed



41
42
43
44
45
46
# File 'lib/simple_nested_set/nested_set.rb', line 41

def denormalize!
  sql = []
  sql << denormalize_level_query if node.has_attribute?(:level)
  sql << denormalize_path_query  if node.has_attribute?(:path)
  update_all(sql.join(',')) unless sql.blank?
end

#denormalize_level_queryObject



98
99
100
101
102
103
104
105
# File 'lib/simple_nested_set/nested_set.rb', line 98

def denormalize_level_query
  query = arel_table.as(:l)
  query = query.project('count(id)').
          where(query[:lft].lt(arel_table[:lft])).
          where(query[:rgt].gt(arel_table[:rgt])).
          where(where_clauses.map { |clause| clause.gsub(table_name, 'l') })
  "level = (#{query.to_sql})"
end

#denormalize_path_queryObject



107
108
109
110
111
112
113
114
# File 'lib/simple_nested_set/nested_set.rb', line 107

def denormalize_path_query
  query = arel_table.as(:l)
  query = query.project("GROUP_CONCAT(slug, '/')").
          where(query[:lft].lteq(arel_table[:lft])).
          where(query[:rgt].gteq(arel_table[:rgt])).
          where(where_clauses.map { |clause| clause.gsub(table_name, 'l') })
  "path = (#{query.to_sql})"
end

#init_as_nodeObject

before validation set lft and rgt to the end of the tree



72
73
74
75
# File 'lib/simple_nested_set/nested_set.rb', line 72

def init_as_node
  max_right = maximum(:rgt) || 0
  node.lft, node.rgt = max_right + 1, max_right + 2
end

#move_by_attributes(attributes) ⇒ Object



90
91
92
# File 'lib/simple_nested_set/nested_set.rb', line 90

def move_by_attributes(attributes)
  Move::ByAttributes.new(node, attributes).perform
end

#move_to(target, position) ⇒ Object



94
95
96
# File 'lib/simple_nested_set/nested_set.rb', line 94

def move_to(target, position)
  Move::ToTarget.new(node, target, position).perform
end

#populate_associations(nodes) ⇒ Object



62
63
64
65
66
67
68
69
# File 'lib/simple_nested_set/nested_set.rb', line 62

def populate_associations(nodes)
  node.children.target = nodes.select do |child|
    next unless child.parent_id == node.id
    nodes.delete(child)
    child.nested_set.populate_associations(nodes)
    child.parent = node
  end
end

#prune_branchObject

Prunes a branch off of the tree, shifting all of the elements on the right back to the left so the counts still work.



79
80
81
82
83
84
85
86
87
88
# File 'lib/simple_nested_set/nested_set.rb', line 79

def prune_branch
  if node.rgt && node.lft
    transaction do
      diff = node.rgt - node.lft + 1
      delete_all(['lft > ? AND rgt < ?', node.lft, node.rgt])
      update_all(['lft = (lft - ?)', diff], ['lft >= ?', node.rgt])
      update_all(['rgt = (rgt - ?)', diff], ['rgt >= ?', node.rgt])
    end
  end
end

#reloadObject

reload left, right, and parent



54
55
56
# File 'lib/simple_nested_set/nested_set.rb', line 54

def reload
  node.reload(:select => attribute_names.join(', ')) unless node.new_record? # FIXME reloading doesn't seem to work?
end

#same_scope?(other) ⇒ Boolean

Returns true if the node has the same scope as the given node

Returns:

  • (Boolean)


49
50
51
# File 'lib/simple_nested_set/nested_set.rb', line 49

def same_scope?(other)
  scope_names.all? { |scope| node.send(scope) == other.send(scope) }
end

#save!Object



32
33
34
35
36
37
# File 'lib/simple_nested_set/nested_set.rb', line 32

def save!
  attributes = node.instance_variable_get(:@_nested_set_attributes)
  node.instance_variable_set(:@_nested_set_attributes, nil)
  move_by_attributes(attributes) unless attributes.blank?
  denormalize!
end