Module: DataMapper::Is::NestedSet::InstanceMethods
- Defined in:
- lib/dm-is-nested_set/is/nested_set.rb
Instance Method Summary collapse
-
#ancestor ⇒ Resource, NilClass
get the parent of this node.
-
#ancestors ⇒ Collection
get all ancestors of this node.
-
#descendants ⇒ Collection
get all descendants of this node.
-
#leaf? ⇒ Boolean
check if this node is a leaf (does not have subnodes).
-
#leaves ⇒ Collection
get all descendants of this node that does not have any children.
-
#left_sibling ⇒ Resource, NilClass
get sibling to the left of/above this node in the nested tree.
-
#level ⇒ Integer
get the level of this node, where 0 is root.
-
#move(vector) ⇒ FalseClass
move self / node to a position in the set.
- #move_without_saving(vector) ⇒ Object
-
#nested_set ⇒ Object
the whole nested set this node belongs to.
- #nested_set_scope ⇒ Object
- #original_nested_set_scope ⇒ Object
-
#right_sibling ⇒ Resource, NilClass
get sibling to the right of/above this node in the nested tree.
-
#root ⇒ Resource, NilClass
get the root this node belongs to.
-
#root? ⇒ Boolean
check if this node is a root.
-
#self_and_ancestors ⇒ Collection
get all ancestors of this node, up to (and including) self.
-
#self_and_descendants ⇒ Collection
get all descendants of this node, including self.
-
#self_and_siblings ⇒ Collection
get all siblings of this node, and include self.
-
#siblings ⇒ Collection
get all siblings of this node.
Instance Method Details
#ancestor ⇒ Resource, NilClass
get the parent of this node. Same as #parent, but finds it from lft/rgt instead of parent-key
282 283 284 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 282 def ancestor ancestors.last end |
#ancestors ⇒ Collection
get all ancestors of this node
273 274 275 276 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 273 def ancestors nested_set.all(:lft.lt => lft, :rgt.gt => rgt) #self_and_ancestors.reject{|r| r.key == key } # because identitymap is not used in console end |
#descendants ⇒ Collection
get all descendants of this node
316 317 318 319 320 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 316 def descendants # TODO add argument for returning as a nested array. # TODO supply filtering-option? nested_set.all(:lft => (lft + 1)..(rgt - 1)) end |
#leaf? ⇒ Boolean
check if this node is a leaf (does not have subnodes). use this instead ofdescendants.empty?
334 335 336 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 334 def leaf? rgt - lft == 1 end |
#leaves ⇒ Collection
get all descendants of this node that does not have any children
326 327 328 329 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 326 def leaves # TODO supply filtering-option? nested_set.all(:lft => (lft + 1)..rgt, :conditions => [ 'rgt = lft + ?', 1 ]) end |
#left_sibling ⇒ Resource, NilClass
get sibling to the left of/above this node in the nested tree
362 363 364 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 362 def left_sibling self_and_siblings.detect { |v| v.rgt == lft - 1 } end |
#level ⇒ Integer
get the level of this node, where 0 is root. temporary solution
255 256 257 258 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 255 def level # TODO make a level-property that is cached and intelligently adjusted when saving objects ancestors.length end |
#move(vector) ⇒ FalseClass
move self / node to a position in the set. position can only be changed through this
168 169 170 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 168 def move(vector) move_without_saving(vector) && save end |
#move_without_saving(vector) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 174 def move_without_saving(vector) if vector.kind_of?(Hash) action, object = vector.keys[0], vector.values[0] else action = vector end changed_scope = nested_set_scope != original_nested_set_scope position = case action when :higher then left_sibling ? left_sibling.lft : nil # : "already at the top" when :highest then ancestor ? ancestor.lft + 1 : nil # : "is root, or has no parent" when :lower then right_sibling ? right_sibling.rgt + 1 : nil # : "already at the bottom" when :lowest then ancestor ? ancestor.rgt : nil # : "is root, or has no parent" when :indent then left_sibling ? left_sibling.rgt : nil # : "cannot find a sibling to indent into" when :outdent then ancestor ? ancestor.rgt + 1 : nil # : "is root, or has no parent" when :into then object ? object.rgt : nil # : "supply an object" when :above then object ? object.lft : nil # : "supply an object" when :below then object ? object.rgt + 1 : nil # : "supply an object" when :to then object ? object.to_i : nil # : "supply a number" end ## # raising an error whenever it couldnt move seems a bit harsh. want to return self for nesting. # if anyone has a good idea about how it should react when it cant set a valid position, # don't hesitate to find me in #datamapper, or send me an email at sindre -a- identu -dot- no # # raise UnableToPositionError unless position.is_a?(Integer) && position > 0 return false if !position || position < 1 # return false if you are trying to move this into another scope return false if [ :into, :above, :below ].include?(action) && nested_set_scope != object.nested_set_scope # if node is already in the requested position if lft == position || rgt == position - 1 self.parent = ancestor # must set this again, because it might have been changed by the user before move. return false end ## # if this node is already positioned we need to move it, and close the gap it leaves behind etc # otherwise we only need to open a gap in the set, and smash that buggar in # if lft && rgt # raise exception if node is trying to move into one of its descendants (infinite loop, spacetime will warp) raise RecursiveNestingError if position > lft && position < rgt # find out how wide this node is, as we need to make a gap large enough for it to fit in gap = rgt - lft + 1 # make a gap at position, that is as wide as this node model.base_model.adjust_gap!(nested_set, position - 1, gap) eager_props = model.properties.values_at(:lft, :rgt) # FIXME don't use @api private # offset this node (and all its descendants) to the right position eager_load(eager_props) old_position = lft offset = position - old_position nested_set.all(:rgt => lft..rgt).adjust!({ :lft => offset, :rgt => offset }, true) # close the gap this movement left behind. model.base_model.adjust_gap!(nested_set, old_position, -gap) # FIXME don't use @api private eager_load(eager_props) else # make a gap where the new node can be inserted model.base_model.adjust_gap!(nested_set, position - 1, 2) # set the position fields self.lft, self.rgt = position, position + 1 end self.parent = ancestor end |
#nested_set ⇒ Object
the whole nested set this node belongs to. served flat like a pancake!
137 138 139 140 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 137 def nested_set # TODO add option for serving it as a nested array model.base_model.all(nested_set_scope.merge(:order => [ :lft ])) end |
#nested_set_scope ⇒ Object
119 120 121 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 119 def nested_set_scope Hash[ model.base_model.nested_set_scope.map { |p| [ p, attribute_get(p) ] } ] end |
#original_nested_set_scope ⇒ Object
126 127 128 129 130 131 132 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 126 def original_nested_set_scope # TODO commit pairs = model.base_model.nested_set_scope.map do |p| [ p, (property = properties[p]) && original_attributes.key?(property) ? original_attributes[property] : attribute_get(p) ] end Hash[ pairs ] end |
#right_sibling ⇒ Resource, NilClass
get sibling to the right of/above this node in the nested tree
371 372 373 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 371 def right_sibling self_and_siblings.detect { |v| v.lft == rgt + 1 } end |
#root ⇒ Resource, NilClass
get the root this node belongs to. this will atm always be the same as Resource.root, but has a meaning when scoped sets is implemented
291 292 293 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 291 def root nested_set.first end |
#root? ⇒ Boolean
check if this node is a root
298 299 300 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 298 def root? !parent && !new? end |
#self_and_ancestors ⇒ Collection
get all ancestors of this node, up to (and including) self
264 265 266 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 264 def self_and_ancestors nested_set.all(:lft.lte => lft, :rgt.gte => rgt) end |
#self_and_descendants ⇒ Collection
get all descendants of this node, including self
306 307 308 309 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 306 def self_and_descendants # TODO supply filtering-option? nested_set.all(:lft => lft..rgt) end |
#self_and_siblings ⇒ Collection
get all siblings of this node, and include self
342 343 344 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 342 def self_and_siblings parent ? parent.children : [ self ] end |
#siblings ⇒ Collection
get all siblings of this node
351 352 353 354 355 |
# File 'lib/dm-is-nested_set/is/nested_set.rb', line 351 def siblings # TODO find a way to return this as a collection? # TODO supply filtering-option? self_and_siblings.reject { |r| r.key == key } # because identitymap is not used in console end |