summaryrefslogtreecommitdiffstats
path: root/lib/redmine/nested_set/traversing.rb
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2015-01-07 20:19:49 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2015-01-07 20:19:49 +0000
commit1a851318fdce55a7ffb2290de692282e294987f8 (patch)
tree6116e0043b56ed114334d5e8656b61d4a8b9666e /lib/redmine/nested_set/traversing.rb
parentbf5d58a76887c2d7819d9f4a1e28139de0ddc95c (diff)
downloadredmine-1a851318fdce55a7ffb2290de692282e294987f8.tar.gz
redmine-1a851318fdce55a7ffb2290de692282e294987f8.zip
Replaces awesome_nested_set gem with a simple and more robust implementation of nested sets.
The concurrency tests added in this commit trigger dead locks and/or nested set inconsistency with awesome_nested_set. git-svn-id: http://svn.redmine.org/redmine/trunk@13841 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'lib/redmine/nested_set/traversing.rb')
-rw-r--r--lib/redmine/nested_set/traversing.rb116
1 files changed, 116 insertions, 0 deletions
diff --git a/lib/redmine/nested_set/traversing.rb b/lib/redmine/nested_set/traversing.rb
new file mode 100644
index 000000000..f1684c00b
--- /dev/null
+++ b/lib/redmine/nested_set/traversing.rb
@@ -0,0 +1,116 @@
+# Redmine - project management software
+# Copyright (C) 2006-2014 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+module Redmine
+ module NestedSet
+ module Traversing
+ def self.included(base)
+ base.class_eval do
+ scope :roots, lambda {where :parent_id => nil}
+ scope :leaves, lambda {where "#{table_name}.rgt - #{table_name}.lft = ?", 1}
+ end
+ end
+
+ # Returns true if the element has no parent
+ def root?
+ parent_id.nil?
+ end
+
+ # Returns true if the element has a parent
+ def child?
+ !root?
+ end
+
+ # Returns true if the element has no children
+ def leaf?
+ new_record? || (rgt - lft == 1)
+ end
+
+ # Returns the root element (ancestor with no parent)
+ def root
+ self_and_ancestors.first
+ end
+
+ # Returns the children
+ def children
+ if id.nil?
+ nested_set_scope.none
+ else
+ self.class.order(:lft).where(:parent_id => id)
+ end
+ end
+
+ # Returns the descendants that have no children
+ def leaves
+ descendants.where("#{self.class.table_name}.rgt - #{self.class.table_name}.lft = ?", 1)
+ end
+
+ # Returns the siblings
+ def siblings
+ nested_set_scope.where(:parent_id => parent_id).where("id <> ?", id)
+ end
+
+ # Returns the ancestors
+ def ancestors
+ if root?
+ nested_set_scope.none
+ else
+ nested_set_scope.where("#{self.class.table_name}.lft < ? AND #{self.class.table_name}.rgt > ?", lft, rgt)
+ end
+ end
+
+ # Returns the element and its ancestors
+ def self_and_ancestors
+ nested_set_scope.where("#{self.class.table_name}.lft <= ? AND #{self.class.table_name}.rgt >= ?", lft, rgt)
+ end
+
+ # Returns true if the element is an ancestor of other
+ def is_ancestor_of?(other)
+ same_nested_set_scope?(other) && other.lft > lft && other.rgt < rgt
+ end
+
+ # Returns true if the element equals other or is an ancestor of other
+ def is_or_is_ancestor_of?(other)
+ other == self || is_ancestor_of?(other)
+ end
+
+ # Returns the descendants
+ def descendants
+ if leaf?
+ nested_set_scope.none
+ else
+ nested_set_scope.where("#{self.class.table_name}.lft > ? AND #{self.class.table_name}.rgt < ?", lft, rgt)
+ end
+ end
+
+ # Returns the element and its descendants
+ def self_and_descendants
+ nested_set_scope.where("#{self.class.table_name}.lft >= ? AND #{self.class.table_name}.rgt <= ?", lft, rgt)
+ end
+
+ # Returns true if the element is a descendant of other
+ def is_descendant_of?(other)
+ same_nested_set_scope?(other) && other.lft < lft && other.rgt > rgt
+ end
+
+ # Returns true if the element equals other or is a descendant of other
+ def is_or_is_descendant_of?(other)
+ other == self || is_descendant_of?(other)
+ end
+ end
+ end
+end