Class: SimpleNestedSet::NestedSet
- Inherits:
-
ActiveRecord::Relation
- Object
- ActiveRecord::Relation
- SimpleNestedSet::NestedSet
- Includes:
- SqlAbstraction
- Defined in:
- lib/simple_nested_set/nested_set.rb
Instance Attribute Summary collapse
-
#node ⇒ Object
readonly
Returns the value of attribute node.
Class Method Summary collapse
- .build_class(model, scopes) ⇒ Object
- .extract_attributes!(attributes) ⇒ Object
- .scope(scope) ⇒ Object
- .scope_condition(scope) ⇒ Object
Instance Method Summary collapse
- #attribute_names ⇒ Object
- #db_adapter ⇒ Object
-
#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.
- #denormalize_level_query ⇒ Object
- #denormalize_path_query ⇒ Object
-
#init_as_node ⇒ Object
before validation set lft and rgt to the end of the tree.
-
#initialize(*args) ⇒ NestedSet
constructor
A new instance of NestedSet.
- #move_by_attributes(attributes) ⇒ Object
- #move_to(target, position) ⇒ Object
- #move_to_path(path) ⇒ Object
- #populate_associations(nodes) ⇒ Object
-
#prune_branch ⇒ Object
Prunes a branch off of the tree, shifting all of the elements on the right back to the left so the counts still work.
- #rebuild_from_paths! ⇒ Object
-
#reload ⇒ Object
reload nested set attributes.
-
#same_scope?(other) ⇒ Boolean
Returns true if the node has the same scope as the given node.
- #save! ⇒ Object
Methods included from SqlAbstraction
Constructor Details
#initialize(*args) ⇒ NestedSet
Returns a new instance of NestedSet.
34 35 36 37 38 |
# File 'lib/simple_nested_set/nested_set.rb', line 34 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
#node ⇒ Object (readonly)
Returns the value of attribute node.
32 33 34 |
# File 'lib/simple_nested_set/nested_set.rb', line 32 def node @node end |
Class Method Details
.build_class(model, scopes) ⇒ Object
8 9 10 11 12 13 |
# File 'lib/simple_nested_set/nested_set.rb', line 8 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 |
.extract_attributes!(attributes) ⇒ Object
25 26 27 28 29 |
# File 'lib/simple_nested_set/nested_set.rb', line 25 def extract_attributes!(attributes) attributes.slice(*SimpleNestedSet::ATTRIBUTES).tap do attributes.except!(*(SimpleNestedSet::ATTRIBUTES - [:path])) end if attributes.respond_to?(:slice) end |
.scope(scope) ⇒ Object
15 16 17 |
# File 'lib/simple_nested_set/nested_set.rb', line 15 def scope(scope) scope.blank? ? node_class.scoped : node_class.where(scope_condition(scope)) end |
.scope_condition(scope) ⇒ Object
19 20 21 22 23 |
# File 'lib/simple_nested_set/nested_set.rb', line 19 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_names ⇒ Object
73 74 75 |
# File 'lib/simple_nested_set/nested_set.rb', line 73 def attribute_names @attribute_names ||= node.attribute_names.select { |attribute| ATTRIBUTES.include?(attribute.to_sym) } end |
#db_adapter ⇒ Object
141 142 143 |
# File 'lib/simple_nested_set/nested_set.rb', line 141 def db_adapter node.class.connection.instance_variable_get('@config')[:adapter].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
49 50 51 52 53 54 |
# File 'lib/simple_nested_set/nested_set.rb', line 49 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_query ⇒ Object
123 124 125 126 127 128 129 130 |
# File 'lib/simple_nested_set/nested_set.rb', line 123 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_query ⇒ Object
132 133 134 135 136 137 138 139 |
# File 'lib/simple_nested_set/nested_set.rb', line 132 def denormalize_path_query query = arel_table.as(:l) query = query.project(group_concat(db_adapter, :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_node ⇒ Object
before validation set lft and rgt to the end of the tree
87 88 89 90 |
# File 'lib/simple_nested_set/nested_set.rb', line 87 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
105 106 107 |
# File 'lib/simple_nested_set/nested_set.rb', line 105 def move_by_attributes(attributes) Move::ByAttributes.new(node, attributes).perform end |
#move_to(target, position) ⇒ Object
115 116 117 |
# File 'lib/simple_nested_set/nested_set.rb', line 115 def move_to(target, position) Move::ToTarget.new(node, target, position).perform end |
#move_to_path(path) ⇒ Object
109 110 111 112 113 |
# File 'lib/simple_nested_set/nested_set.rb', line 109 def move_to_path(path) node.path, parent_path = path, path.split('/')[0..-2].join('/') parent = parent_path.empty? ? nil : node.nested_set.where(:path => parent_path).first node.move_to_child_of(parent) end |
#populate_associations(nodes) ⇒ Object
77 78 79 80 81 82 83 84 |
# File 'lib/simple_nested_set/nested_set.rb', line 77 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_branch ⇒ Object
Prunes a branch off of the tree, shifting all of the elements on the right back to the left so the counts still work.
94 95 96 97 98 99 100 101 102 103 |
# File 'lib/simple_nested_set/nested_set.rb', line 94 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 |
#rebuild_from_paths! ⇒ Object
119 120 121 |
# File 'lib/simple_nested_set/nested_set.rb', line 119 def rebuild_from_paths! Rebuild::FromPaths.new.run(self) end |
#reload ⇒ Object
reload nested set attributes
62 63 64 65 66 67 68 69 70 71 |
# File 'lib/simple_nested_set/nested_set.rb', line 62 def reload columns = [:parent_id, :lft, :rgt] columns << :level if node.has_attribute?(:level) columns << :path if node.has_attribute?(:path) reloaded = unscoped { find(node.id, :select => columns) } node.instance_eval { @attributes.merge!(reloaded.instance_variable_get(:@attributes)) } node.parent = nil if node.parent_id.nil? node.children.reset end |
#same_scope?(other) ⇒ Boolean
Returns true if the node has the same scope as the given node
57 58 59 |
# File 'lib/simple_nested_set/nested_set.rb', line 57 def same_scope?(other) scope_names.all? { |scope| node.send(scope) == other.send(scope) } end |
#save! ⇒ Object
40 41 42 43 44 45 |
# File 'lib/simple_nested_set/nested_set.rb', line 40 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 |