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_parents!(sort_order = nil) ⇒ Object
- #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.
35 36 37 38 39 40 |
# File 'lib/simple_nested_set/nested_set.rb', line 35 def initialize(*args) super(node_class, node_class.arel_table) @node = args.first if args.size == 1 @where_values = self.class.scope(args.first).instance_variable_get(:@where_values) if args.size == 1 # TODO how to set order(:lft) here? it's now being added on various scopes (see class methods), would be better to have it here. end |
Instance Attribute Details
#node ⇒ Object (readonly)
Returns the value of attribute node.
33 34 35 |
# File 'lib/simple_nested_set/nested_set.rb', line 33 def node @node end |
Class Method Details
.build_class(model, scopes) ⇒ Object
8 9 10 11 12 13 14 |
# 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.move_after_save = true node_class.scope_names = Array(scopes).map { |s| s.to_s =~ /_id$/ ? s.to_sym : :"#{s}_id" } end end |
.extract_attributes!(attributes) ⇒ Object
26 27 28 29 30 |
# File 'lib/simple_nested_set/nested_set.rb', line 26 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
16 17 18 |
# File 'lib/simple_nested_set/nested_set.rb', line 16 def scope(scope) scope.blank? ? node_class.scoped : node_class.where(scope_condition(scope)) end |
.scope_condition(scope) ⇒ Object
20 21 22 23 24 |
# File 'lib/simple_nested_set/nested_set.rb', line 20 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
82 83 84 |
# File 'lib/simple_nested_set/nested_set.rb', line 82 def attribute_names @attribute_names ||= node.attribute_names.select { |attribute| ATTRIBUTES.include?(attribute.to_sym) } end |
#db_adapter ⇒ Object
154 155 156 |
# File 'lib/simple_nested_set/nested_set.rb', line 154 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
58 59 60 61 62 63 |
# File 'lib/simple_nested_set/nested_set.rb', line 58 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
136 137 138 139 140 141 142 143 |
# File 'lib/simple_nested_set/nested_set.rb', line 136 def denormalize_level_query aliaz = arel_table.as(:l) query = aliaz.project(aliaz[:id].count). where(aliaz[:lft].lt(arel_table[:lft])). where(aliaz[:rgt].gt(arel_table[:rgt])) query = [query.to_sql] + where_clauses.map { |clause| clause.gsub(arel_table.name, 'l') } "level = (#{query.join(' AND ')})" end |
#denormalize_path_query ⇒ Object
145 146 147 148 149 150 151 152 |
# File 'lib/simple_nested_set/nested_set.rb', line 145 def denormalize_path_query aliaz = arel_table.as(:l) query = aliaz.project(group_concat(db_adapter, :slug)). where(aliaz[:lft].lteq(arel_table[:lft])). where(aliaz[:rgt].gteq(arel_table[:rgt])) query = [query.to_sql] + where_clauses.map { |clause| clause.gsub(arel_table.name, 'l') } "path = (#{query.join(' AND ')})" end |
#init_as_node ⇒ Object
before validation set lft and rgt to the end of the tree
96 97 98 99 |
# File 'lib/simple_nested_set/nested_set.rb', line 96 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
114 115 116 |
# File 'lib/simple_nested_set/nested_set.rb', line 114 def move_by_attributes(attributes) Move::ByAttributes.new(node, attributes).perform end |
#move_to(target, position) ⇒ Object
124 125 126 |
# File 'lib/simple_nested_set/nested_set.rb', line 124 def move_to(target, position) Move::ToTarget.new(node, target, position).perform end |
#move_to_path(path) ⇒ Object
118 119 120 121 122 |
# File 'lib/simple_nested_set/nested_set.rb', line 118 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
86 87 88 89 90 91 92 93 |
# File 'lib/simple_nested_set/nested_set.rb', line 86 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.
103 104 105 106 107 108 109 110 111 112 |
# File 'lib/simple_nested_set/nested_set.rb', line 103 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_parents!(sort_order = nil) ⇒ Object
132 133 134 |
# File 'lib/simple_nested_set/nested_set.rb', line 132 def rebuild_from_parents!(sort_order = nil) Rebuild::FromParents.new.run(self, sort_order) end |
#rebuild_from_paths! ⇒ Object
128 129 130 |
# File 'lib/simple_nested_set/nested_set.rb', line 128 def rebuild_from_paths! Rebuild::FromPaths.new.run(self) end |
#reload ⇒ Object
reload nested set attributes
71 72 73 74 75 76 77 78 79 80 |
# File 'lib/simple_nested_set/nested_set.rb', line 71 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
66 67 68 |
# File 'lib/simple_nested_set/nested_set.rb', line 66 def same_scope?(other) scope_names.all? { |scope| node.send(scope) == other.send(scope) } end |
#save! ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/simple_nested_set/nested_set.rb', line 42 def save! attributes = node.instance_variable_get(:@_nested_set_attributes) node.instance_variable_set(:@_nested_set_attributes, nil) if self.class.move_after_save move_by_attributes(attributes) unless attributes.blank? denormalize! elsif attributes attributes.each do |key, value| node.update_attribute(key, value) end end end |